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

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Becker <halfdan@xnorfz.de>2013-06-22 23:47:29 +0400
committerFabian Becker <halfdan@xnorfz.de>2013-06-22 23:47:29 +0400
commit165ac7aeec855234e8c60bd758fad39bd6659adf (patch)
treee42ace9f7f45e420f883245a8df1beefa8eac9b0 /plugins
parent89d41940a87d449114f529df115b62d3987f88ce (diff)
parente2e5176279255b7ff2d80f0f511a1ef5f3748ebe (diff)
Huge merge! Lets see how the tests run..
Merge branch 'master' into 2.x-twig Conflicts: core/ReportRenderer/Html.php core/SmartyPlugins/function.ajaxLoadingDiv.php plugins/CoreAdminHome/templates/jsTrackingGenerator.tpl plugins/CoreHome/templates/donate.tpl plugins/CoreHome/templates/html_report_header.tpl plugins/CoreHome/templates/menu.tpl plugins/CoreHome/templates/period_select.tpl plugins/CoreHome/templates/reports_by_dimension.tpl plugins/Feedback/templates/index.tpl plugins/Goals/Controller.php plugins/Goals/templates/overview.tpl plugins/Live/API.php plugins/Live/templates/lastVisits.tpl plugins/Live/templates/visitorLog.tpl plugins/MobileMessaging/templates/SMSReport.tpl plugins/PDFReports/templates/add.tpl plugins/PDFReports/templates/list.tpl plugins/PDFReports/templates/report_parameters.tpl plugins/SEO/templates/index.tpl plugins/SegmentEditor/templates/selector.twig plugins/UserCountry/javascripts/userCountry.js plugins/Zeitgeist/stylesheets/common.css tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__UserCountry.getCity_month.xml tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__UserCountry.getCountry_month.xml tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__UserCountry.getRegion_month.xml tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_segment_continent__UserCountry.getCountry_month.xml
Diffstat (limited to 'plugins')
-rw-r--r--plugins/API/API.php55
-rw-r--r--plugins/Actions/API.php82
-rw-r--r--plugins/Actions/Actions.php34
-rw-r--r--plugins/Actions/Archiver.php (renamed from plugins/Actions/Archiving.php)622
-rw-r--r--plugins/Actions/ArchivingHelper.php50
-rwxr-xr-xplugins/Annotations/javascripts/annotations.js5
-rw-r--r--plugins/CoreAdminHome/API.php7
-rw-r--r--plugins/CoreAdminHome/Controller.php8
-rw-r--r--plugins/CoreAdminHome/CoreAdminHome.php10
-rw-r--r--plugins/CoreAdminHome/javascripts/generalSettings.js7
-rw-r--r--plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js23
-rw-r--r--plugins/CoreAdminHome/templates/jsTrackingGenerator.twig2
-rw-r--r--plugins/CoreHome/Controller.php21
-rw-r--r--plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php3
-rw-r--r--plugins/CoreHome/DataTableRowAction/RowEvolution.php9
-rw-r--r--plugins/CoreHome/javascripts/autocomplete.js26
-rw-r--r--plugins/CoreHome/javascripts/broadcast.js46
-rw-r--r--plugins/CoreHome/javascripts/calendar.js12
-rwxr-xr-xplugins/CoreHome/javascripts/corehome.js3
-rw-r--r--plugins/CoreHome/javascripts/datatable.js7
-rw-r--r--plugins/CoreHome/javascripts/datatable_rowactions.js1
-rw-r--r--plugins/CoreHome/javascripts/date.js18
-rw-r--r--plugins/CoreHome/javascripts/menu.js4
-rwxr-xr-xplugins/CoreHome/templates/donate.twig4
-rw-r--r--plugins/CoreHome/templates/html_report_header.twig6
-rw-r--r--plugins/CoreHome/templates/menu.twig6
-rw-r--r--plugins/CoreHome/templates/period_select.twig1
-rw-r--r--plugins/CoreHome/templates/reports_by_dimension.twig2
-rw-r--r--plugins/CoreUpdater/Controller.php2
-rw-r--r--plugins/CustomVariables/API.php18
-rw-r--r--plugins/CustomVariables/Archiver.php198
-rw-r--r--plugins/CustomVariables/Controller.php29
-rw-r--r--plugins/CustomVariables/CustomVariables.php177
-rw-r--r--plugins/DBStats/API.php4
-rwxr-xr-xplugins/DBStats/MySQLMetadataProvider.php1
-rw-r--r--plugins/Dashboard/API.php4
-rw-r--r--plugins/Dashboard/Controller.php3
-rw-r--r--plugins/Dashboard/Dashboard.php1
-rw-r--r--plugins/Dashboard/javascripts/dashboard.js26
-rw-r--r--plugins/Dashboard/javascripts/dashboardObject.js2
-rw-r--r--plugins/DevicesDetection/API.php161
-rw-r--r--plugins/DevicesDetection/Archiver.php64
-rw-r--r--plugins/DevicesDetection/Controller.php177
-rw-r--r--plugins/DevicesDetection/DevicesDetection.php264
-rw-r--r--plugins/DevicesDetection/UserAgentParserEnhanced/UserAgentParserEnhanced.php735
-rw-r--r--plugins/DevicesDetection/UserAgentParserEnhanced/regexes/browsers.yml408
-rw-r--r--plugins/DevicesDetection/UserAgentParserEnhanced/regexes/mobiles.yml958
-rw-r--r--plugins/DevicesDetection/UserAgentParserEnhanced/regexes/oss.yml427
-rw-r--r--plugins/DevicesDetection/functions.php149
-rw-r--r--plugins/DevicesDetection/images/brand/Acer.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Alcatel.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Apple.icobin0 -> 2982 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Asus.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Audiovox.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Avvio.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Becker.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Beetel.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/BenQ.icobin0 -> 2326 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Compal.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Cricket.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Dell.icobin0 -> 1494 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/DoCoMo.icobin0 -> 1494 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Ericsson.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Google.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Gradiente.icobin0 -> 1910 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Grundig.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/HP.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/HTC.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Haier.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Huawei.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/INQ.icobin0 -> 1095 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/KDDI.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Karbonn.icobin0 -> 894 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Kindle.icobin0 -> 2254 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Kyocera.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/LG.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/LGUPlus.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Lanix.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Lenovo.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/MicroMax.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Microsoft.icobin0 -> 2110 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Mio.icobin0 -> 2326 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Mitsubishi.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Motorola.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/MyPhone.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/NGM.icobin0 -> 1214 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Nintendo.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Nokia.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/O2.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/OPPO.icobin0 -> 2326 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Onda.icobin0 -> 894 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Orange.icobin0 -> 1145 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Palm.icobin0 -> 631 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Panasonic.icobin0 -> 3998 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Pantech.icobin0 -> 3201 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Philips.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/RIM.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Samsung.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Sanyo.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Sega.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Softbank.icobin0 -> 1910 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Sony Ericsson.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Sony.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/T-Mobile.icobin0 -> 1406 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Telit.icobin0 -> 2550 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/TiPhone.icobin0 -> 2582 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Vertu.icobin0 -> 2946 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Videocon.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/Zonda.icobin0 -> 295 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/eTouch.icobin0 -> 2254 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/i-mobile.icobin0 -> 2582 bytes
-rw-r--r--plugins/DevicesDetection/images/brand/unknown.icobin0 -> 1150 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/computer.pngbin0 -> 550 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/console.gifbin0 -> 617 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/dual.gifbin0 -> 1082 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/mobile.gifbin0 -> 324 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/normal.gifbin0 -> 1088 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/smartphone.pngbin0 -> 547 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/tablet.pngbin0 -> 602 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/tv.pngbin0 -> 644 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/unknown.gifbin0 -> 80 bytes
-rw-r--r--plugins/DevicesDetection/images/screens/wide.gifbin0 -> 1025 bytes
-rw-r--r--plugins/DevicesDetection/lang/en.php29
-rw-r--r--plugins/DevicesDetection/templates/index.tpl15
-rw-r--r--plugins/ExampleUI/API.php15
-rw-r--r--plugins/Feedback/Feedback.php2
-rw-r--r--plugins/Feedback/javascripts/feedback.js2
-rw-r--r--plugins/Feedback/templates/index.twig2
-rw-r--r--plugins/Goals/API.php49
-rw-r--r--plugins/Goals/Archiver.php424
-rw-r--r--plugins/Goals/Controller.php45
-rw-r--r--plugins/Goals/Goals.php549
-rw-r--r--plugins/Goals/javascripts/goalsForm.js2
-rw-r--r--plugins/Goals/templates/overview.twig2
-rw-r--r--plugins/ImageGraph/API.php9
-rw-r--r--plugins/ImageGraph/ImageGraph.php14
-rw-r--r--plugins/ImageGraph/StaticGraph/HorizontalBar.php12
-rw-r--r--plugins/ImageGraph/StaticGraph/PieGraph.php2
-rw-r--r--plugins/Installation/Controller.php7
-rw-r--r--plugins/Live/API.php20
-rw-r--r--plugins/Live/Controller.php2
-rw-r--r--plugins/Live/Visitor.php27
-rw-r--r--plugins/Live/templates/lastVisits.twig2
-rw-r--r--plugins/Live/templates/visitorLog.twig6
-rw-r--r--plugins/MobileMessaging/ReportRenderer/Exception.php2
-rw-r--r--plugins/MobileMessaging/ReportRenderer/Sms.php10
-rw-r--r--plugins/MobileMessaging/templates/SMSReport.twig6
-rwxr-xr-xplugins/MultiSites/API.php34
-rw-r--r--plugins/MultiSites/Controller.php42
-rw-r--r--plugins/MultiSites/MultiSites.php2
-rw-r--r--plugins/PDFReports/API.php73
-rw-r--r--plugins/PDFReports/Controller.php11
-rw-r--r--plugins/PDFReports/PDFReports.php94
-rw-r--r--plugins/PDFReports/javascripts/pdf.js12
-rw-r--r--plugins/PDFReports/templates/add.twig19
-rw-r--r--plugins/PDFReports/templates/list.twig11
-rw-r--r--plugins/PDFReports/templates/report_parameters.twig6
-rw-r--r--plugins/PrivacyManager/PrivacyManager.php10
-rwxr-xr-xplugins/PrivacyManager/ReportsPurger.php35
-rw-r--r--plugins/PrivacyManager/javascripts/privacySettings.js8
-rw-r--r--plugins/Provider/API.php4
-rw-r--r--plugins/Provider/Archiver.php27
-rw-r--r--plugins/Provider/Provider.php66
-rw-r--r--plugins/Referers/API.php65
-rw-r--r--plugins/Referers/Archiver.php265
-rw-r--r--plugins/Referers/Controller.php2
-rw-r--r--plugins/Referers/Referers.php302
-rw-r--r--plugins/Referers/functions.php2
-rw-r--r--plugins/Referers/images/searchEngines/search.imesh.com.pngbin0 -> 644 bytes
-rw-r--r--plugins/Referers/images/searchEngines/search.snap.do.pngbin0 -> 570 bytes
-rw-r--r--plugins/Referers/images/searchEngines/www.talimba.com.pngbin0 -> 735 bytes
-rw-r--r--plugins/Referers/images/searchEngines/www.trusted-search.com.png (renamed from plugins/Referers/images/searchEngines/www.trusted--search.com.png)bin736 -> 736 bytes
-rw-r--r--plugins/SEO/API.php4
-rw-r--r--plugins/SEO/templates/index.twig29
-rw-r--r--plugins/SegmentEditor/API.php164
-rw-r--r--plugins/SegmentEditor/Controller.php50
-rw-r--r--plugins/SegmentEditor/SegmentEditor.php8
-rw-r--r--plugins/SegmentEditor/images/dashboard_h_bg_hover.pngbin333 -> 378 bytes
-rw-r--r--plugins/SegmentEditor/javascripts/Segmentation.js354
-rw-r--r--plugins/SegmentEditor/stylesheets/segmentation.css29
-rw-r--r--plugins/SegmentEditor/templates/selector.twig90
-rw-r--r--plugins/SitesManager/javascripts/SitesManager.js19
-rw-r--r--plugins/Transitions/API.php394
-rw-r--r--plugins/Transitions/Transitions.php331
-rw-r--r--plugins/UserCountry/API.php23
-rw-r--r--plugins/UserCountry/Archiver.php175
-rw-r--r--plugins/UserCountry/UserCountry.php205
-rw-r--r--plugins/UserCountry/functions.php6
-rwxr-xr-xplugins/UserCountry/javascripts/userCountry.js55
-rw-r--r--plugins/UserCountryMap/Controller.php41
-rw-r--r--plugins/UserCountryMap/javascripts/realtime-map.js4
-rw-r--r--plugins/UserCountryMap/javascripts/visitor-map.js2
-rw-r--r--plugins/UserCountryMap/stylesheets/visitor-map.css2
-rw-r--r--plugins/UserSettings/API.php88
-rw-r--r--plugins/UserSettings/Archiver.php152
-rw-r--r--plugins/UserSettings/UserSettings.php184
-rw-r--r--plugins/UserSettings/functions.php15
-rw-r--r--plugins/UserSettings/images/browsers/CM.gifbin0 -> 1074 bytes
-rw-r--r--plugins/UserSettings/images/browsers/NB.gifbin0 -> 977 bytes
-rw-r--r--plugins/UserSettings/images/browsers/UN.gifbin0 -> 80 bytes
-rw-r--r--plugins/UserSettings/images/os/COS.gifbin0 -> 1074 bytes
-rw-r--r--plugins/UserSettings/images/os/GTV.gifbin0 -> 1614 bytes
-rw-r--r--plugins/UserSettings/images/os/IOS.gifbin0 -> 591 bytes
-rw-r--r--plugins/UserSettings/images/os/WIN.gifbin0 -> 191 bytes
-rw-r--r--plugins/UsersManager/javascripts/usersManager.js2
-rw-r--r--plugins/UsersManager/javascripts/usersSettings.js6
-rw-r--r--plugins/VisitFrequency/API.php137
-rw-r--r--plugins/VisitFrequency/VisitFrequency.php70
-rw-r--r--plugins/VisitTime/API.php31
-rw-r--r--plugins/VisitTime/Archiver.php80
-rw-r--r--plugins/VisitTime/Controller.php18
-rw-r--r--plugins/VisitTime/VisitTime.php95
-rw-r--r--plugins/VisitorGenerator/Controller.php2
-rw-r--r--plugins/VisitorInterest/API.php13
-rw-r--r--plugins/VisitorInterest/Archiver.php146
-rw-r--r--plugins/VisitorInterest/VisitorInterest.php184
-rw-r--r--plugins/VisitsSummary/API.php48
-rw-r--r--plugins/VisitsSummary/Controller.php2
-rw-r--r--plugins/VisitsSummary/VisitsSummary.php2
-rw-r--r--plugins/Widgetize/Widgetize.php2
-rw-r--r--plugins/Zeitgeist/javascripts/ajaxHelper.js91
-rw-r--r--plugins/Zeitgeist/javascripts/piwikHelper.js52
-rw-r--r--plugins/Zeitgeist/stylesheets/common.css25
223 files changed, 7321 insertions, 3336 deletions
diff --git a/plugins/API/API.php b/plugins/API/API.php
index df4e74e831..3f8d51d82e 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -36,7 +36,7 @@ class Piwik_API extends Piwik_Plugin
public function addTopMenu()
{
- $apiUrlParams = array('module' => 'API', 'action' => 'listAllAPI');
+ $apiUrlParams = array('module' => 'API', 'action' => 'listAllAPI', 'segment' => false);
$tooltip = Piwik_Translate('API_TopLinkTooltip');
Piwik_AddTopMenu('General_API', $apiUrlParams, true, 7, $isHTML = false, $tooltip);
@@ -269,35 +269,38 @@ class Piwik_API_API
$segments = array();
Piwik_PostEvent('API.getSegmentsMetadata', $segments, $idSites);
+ $isAuthenticatedWithViewAccess = Piwik::isUserHasViewAccess($idSites) && !Piwik::isUserIsAnonymous();
+
$segments[] = array(
'type' => 'dimension',
- 'category' => 'Visit',
+ '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' => Piwik::isUserHasAdminAccess($idSites),
+ 'permission' => $isAuthenticatedWithViewAccess,
);
$segments[] = array(
'type' => 'dimension',
- 'category' => 'Visit',
+ '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',
'sqlFilter' => array('Piwik_Common', 'convertVisitorIdToBin'),
+ 'permission' => $isAuthenticatedWithViewAccess,
);
$segments[] = array(
'type' => 'metric',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => 'General_NbActions',
'segment' => 'actions',
'sqlSegment' => 'log_visit.visit_total_actions',
);
$segments[] = array(
'type' => 'metric',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => 'General_NbSearches',
'segment' => 'searches',
'sqlSegment' => 'log_visit.visit_total_searches',
@@ -305,14 +308,14 @@ class Piwik_API_API
);
$segments[] = array(
'type' => 'metric',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => 'General_ColumnVisitDuration',
'segment' => 'visitDuration',
'sqlSegment' => 'log_visit.visit_total_time',
);
$segments[] = array(
'type' => 'dimension',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => Piwik_Translate('General_VisitType') ,
'segment' => 'visitorType',
'acceptedValues' => 'new, returning, returningCustomer' . ". " . Piwik_Translate('General_VisitTypeExample', '"&segment=visitorType==returning,visitorType==returningCustomer"'),
@@ -321,21 +324,21 @@ class Piwik_API_API
);
$segments[] = array(
'type' => 'metric',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => 'General_DaysSinceLastVisit',
'segment' => 'daysSinceLastVisit',
'sqlSegment' => 'log_visit.visitor_days_since_last',
);
$segments[] = array(
'type' => 'metric',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => 'General_DaysSinceFirstVisit',
'segment' => 'daysSinceFirstVisit',
'sqlSegment' => 'log_visit.visitor_days_since_first',
);
$segments[] = array(
'type' => 'metric',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => 'General_NumberOfVisits',
'segment' => 'visitCount',
'sqlSegment' => 'log_visit.visitor_count_visits',
@@ -343,7 +346,7 @@ class Piwik_API_API
$segments[] = array(
'type' => 'dimension',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => 'General_VisitConvertedGoal',
'segment' => 'visitConverted',
'acceptedValues' => '0, 1',
@@ -352,7 +355,7 @@ class Piwik_API_API
$segments[] = array(
'type' => 'dimension',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => Piwik_Translate('General_EcommerceVisitStatusDesc'),
'segment' => 'visitEcommerceStatus',
'acceptedValues' => implode(", ", self::$visitEcommerceStatus)
@@ -363,7 +366,7 @@ class Piwik_API_API
$segments[] = array(
'type' => 'metric',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => 'General_DaysSinceLastEcommerceOrder',
'segment' => 'daysSinceLastEcommerceOrder',
'sqlSegment' => 'log_visit.visitor_days_since_order',
@@ -527,7 +530,7 @@ class Piwik_API_API
$reportsMetadata = $this->getReportMetadata($idSite, $period, $date, $hideMetricsDoc, $showSubtableReports);
foreach ($reportsMetadata as $report) {
- // See ArchiveProcessing/Period.php - unique visitors are not processed for period != day
+ // See ArchiveProcessor/Period.php - unique visitors are not processed for period != day
if (($period && $period != 'day') && !($apiModule == 'VisitsSummary' && $apiAction == 'get')) {
unset($report['metrics']['nb_uniq_visitors']);
}
@@ -740,7 +743,7 @@ class Piwik_API_API
/** @var Piwik_DataTable */
$dataTable = $request->process();
} catch (Exception $e) {
- throw new Exception("API returned an error: " . $e->getMessage() . "\n");
+ throw new Exception("API returned an error: " . $e->getMessage() . " at " . basename($e->getFile()). ":" . $e->getLine() . "\n");
}
list($newReport, $columns, $rowsMetadata) = $this->handleTableReport($idSite, $dataTable, $reportMetadata, isset($reportMetadata['dimension']), $showRawMetrics);
@@ -1035,6 +1038,7 @@ class Piwik_API_API
Piwik_Translate('Referers_Referers'),
Piwik_Translate('Goals_Goals'),
Piwik_Translate('General_Visitors'),
+ Piwik_Translate('DevicesDetection_DevicesDetection'),
Piwik_Translate('UserSettings_VisitorSettings'),
);
}
@@ -1152,20 +1156,16 @@ class Piwik_API_API
public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true)
{
// validation of requested $period & $date
- if ($period == 'range') {
+ if ($period == 'range') {
// load days in the range
$period = 'day';
}
- if (!Piwik_Archive::isMultiplePeriod($date, $period)) {
+ if (!Piwik_Period::isMultiplePeriod($date, $period)) {
throw new Exception("Row evolutions can not be processed with this combination of \'date\' and \'period\' parameters.");
}
- // this is needed because Piwik_API_Proxy uses Piwik_Common::getRequestVar which in turn
- // uses Piwik_Common::sanitizeInputValue. This causes the > that separates recursive labels
- // to become &gt; and we need to undo that here.
- $label = Piwik_Common::unsanitizeInputValue($label);
-
+ $label = Piwik_API_ResponseBuilder::unsanitizeLabelParameter($label);
if ($label) {
$labels = explode(',', $label);
$labels = array_unique($labels);
@@ -1353,7 +1353,7 @@ class Piwik_API_API
// add "processed metrics" like actions per visit or bounce rate
// note: some reports should not be filtered with AddColumnProcessedMetrics
- // specifically, reports without the Piwik_Archive::INDEX_NB_VISITS metric such as Goals.getVisitsUntilConversion & Goal.getDaysToConversion
+ // specifically, reports without the Piwik_Metrics::INDEX_NB_VISITS metric such as Goals.getVisitsUntilConversion & Goal.getDaysToConversion
// this is because the AddColumnProcessedMetrics filter removes all datable rows lacking this metric
if
(
@@ -1618,11 +1618,12 @@ class Piwik_API_API
return array();
}
- $urls = Piwik_Common::unsanitizeInputValues($urls);
+ $urls = array_map('urldecode', $urls);
+ $urls = array_map(array('Piwik_Common','unsanitizeInputValue'), $urls);
$result = array();
foreach ($urls as $url) {
- $req = new Piwik_API_Request($url);
+ $req = new Piwik_API_Request($url . '&format=php&serialize=0');
$result[] = $req->process();
}
return $result;
@@ -1662,7 +1663,7 @@ class Piwik_API_API
// Select non empty fields only
// Note: this optimization has only a very minor impact
- $requestLastVisits.= "&segment=$segmentName" . Piwik_SegmentExpression::MATCH_IS_NOT_NULL . "null";
+ $requestLastVisits.= "&segment=$segmentName".urlencode('!=');
// By default Live fetches all actions for all visitors, but we'd rather do this only when required
if($this->doesSegmentNeedActionsData($segmentName)) {
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php
index 1904b3b72a..8eec6d2d1b 100644
--- a/plugins/Actions/API.php
+++ b/plugins/Actions/API.php
@@ -72,17 +72,8 @@ class Piwik_Actions_API
Piwik::checkUserHasViewAccess($idSite);
$archive = Piwik_Archive::build($idSite, $period, $date, $segment);
- $metrics = array(
- 'Actions_nb_pageviews' => 'nb_pageviews',
- 'Actions_nb_uniq_pageviews' => 'nb_uniq_pageviews',
- 'Actions_nb_downloads' => 'nb_downloads',
- 'Actions_nb_uniq_downloads' => 'nb_uniq_downloads',
- 'Actions_nb_outlinks' => 'nb_outlinks',
- 'Actions_nb_uniq_outlinks' => 'nb_uniq_outlinks',
- 'Actions_nb_searches' => 'nb_searches',
- 'Actions_nb_keywords' => 'nb_keywords',
- 'Actions_avg_time_generation' => 'avg_time_generation'
- );
+ $metrics = Piwik_Actions_Archiver::$actionsAggregateMetrics;
+ $metrics['Actions_avg_time_generation'] = 'avg_time_generation';
// get requested columns
$columns = Piwik::getArrayFromApiParameter($columns);
@@ -96,38 +87,39 @@ class Piwik_Actions_API
$columns[$i] = $fullColumn;
$nameReplace[$fullColumn] = $column;
}
-
- if (false !== ($avgGenerationTimeRequested = array_search('Actions_avg_time_generation', $columns))) {
- unset($columns[$avgGenerationTimeRequested]);
- $avgGenerationTimeRequested = true;
- }
+
+ if (false !== ($avgGenerationTimeRequested = array_search('Actions_avg_time_generation', $columns))) {
+ unset($columns[$avgGenerationTimeRequested]);
+ $avgGenerationTimeRequested = true;
+ }
} else {
// get all columns
- unset($metrics['Actions_avg_time_generation']);
+ unset($metrics['Actions_avg_time_generation']);
$columns = array_keys($metrics);
$nameReplace = & $metrics;
- $avgGenerationTimeRequested = true;
+ $avgGenerationTimeRequested = true;
+ }
+
+ if ($avgGenerationTimeRequested) {
+ $tempColumns[] = Piwik_Actions_Archiver::METRIC_SUM_TIME_RECORD_NAME;
+ $tempColumns[] = Piwik_Actions_Archiver::METRIC_HITS_TIMED_RECORD_NAME;
+ $columns = array_merge($columns, $tempColumns);
+ $columns = array_unique($columns);
+
+ $nameReplace[Piwik_Actions_Archiver::METRIC_SUM_TIME_RECORD_NAME] = 'sum_time_generation';
+ $nameReplace[Piwik_Actions_Archiver::METRIC_HITS_TIMED_RECORD_NAME] = 'nb_hits_with_time_generation';
}
-
- if ($avgGenerationTimeRequested) {
- $tempColumns[] = 'Actions_sum_time_generation';
- $tempColumns[] = 'Actions_nb_hits_with_time_generation';
- $nameReplace['Actions_sum_time_generation'] = 'sum_time_generation';
- $nameReplace['Actions_nb_hits_with_time_generation'] = 'nb_hits_with_time_generation';
- $columns = array_merge($columns, $tempColumns);
- $columns = array_unique($columns);
- }
-
+
$table = $archive->getDataTableFromNumeric($columns);
// replace labels (remove Actions_)
$table->filter('ReplaceColumnNames', array($nameReplace));
-
- // compute avg generation time
- if ($avgGenerationTimeRequested) {
- $table->filter('ColumnCallbackAddColumnQuotient', array('avg_time_generation', 'sum_time_generation', 'nb_hits_with_time_generation', 3));
- $table->deleteColumns(array('sum_time_generation', 'nb_hits_with_time_generation'));
- }
+
+ // compute avg generation time
+ if ($avgGenerationTimeRequested) {
+ $table->filter('ColumnCallbackAddColumnQuotient', array('avg_time_generation', 'sum_time_generation', 'nb_hits_with_time_generation', 3));
+ $table->deleteColumns(array('sum_time_generation', 'nb_hits_with_time_generation'));
+ }
return $table;
}
@@ -301,7 +293,7 @@ class Piwik_Actions_API
public function getSiteSearchKeywords($idSite, $period, $date, $segment = false)
{
$dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment);
- $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT);
+ $dataTable->deleteColumn(Piwik_Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT);
$this->filterPageDatatable($dataTable);
$this->filterActionsDataTable($dataTable);
$this->addPagesPerSearchColumn($dataTable);
@@ -326,11 +318,11 @@ class Piwik_Actions_API
// Delete all rows that have some results
$dataTable->filter('ColumnCallbackDeleteRow',
array(
- Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT,
+ Piwik_Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT,
create_function('$value', 'return $value >= 1;')
));
$dataTable->deleteRow(Piwik_DataTable::ID_SUMMARY_ROW);
- $dataTable->deleteColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT);
+ $dataTable->deleteColumn(Piwik_Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT);
$this->filterPageDatatable($dataTable);
$this->filterActionsDataTable($dataTable);
$this->addPagesPerSearchColumn($dataTable);
@@ -354,7 +346,7 @@ class Piwik_Actions_API
$dataTable = new Piwik_DataTable();
// Handle case where date=last30&period=day
- // TODO: this logic should really be refactored somewhere, this is ugly!
+ // FIXMEA: this logic should really be refactored somewhere, this is ugly!
if ($customVariables instanceof Piwik_DataTable_Array) {
$dataTable = $customVariables->getEmptyClone();
@@ -496,7 +488,7 @@ class Piwik_Actions_API
$dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('exit_rate', 'exit_nb_visits', 'nb_visits', 0));
// Handle performance analytics
- $hasTimeGeneration = (array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION)) > 0);
+ $hasTimeGeneration = (array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_PAGE_SUM_TIME_GENERATION)) > 0);
if ($hasTimeGeneration) {
// Average generation time = total generation time / number of pageviews
$precisionAvgTimeGeneration = 3;
@@ -506,12 +498,12 @@ class Piwik_Actions_API
// No generation time: remove it from the API output and add it to empty_columns metadata, so that
// the columns can also be removed from the view
$dataTable->filter('ColumnDelete', array(array(
- Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION,
- Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION,
- Piwik_Archive::INDEX_PAGE_MIN_TIME_GENERATION,
- Piwik_Archive::INDEX_PAGE_MAX_TIME_GENERATION
- )));
-
+ Piwik_Metrics::INDEX_PAGE_SUM_TIME_GENERATION,
+ Piwik_Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION,
+ Piwik_Metrics::INDEX_PAGE_MIN_TIME_GENERATION,
+ Piwik_Metrics::INDEX_PAGE_MAX_TIME_GENERATION
+ )));
+
if ($dataTable instanceof Piwik_DataTable) {
$emptyColumns = $dataTable->getMetadata(Piwik_DataTable::EMPTY_COLUMNS_METADATA_NAME);
if (!is_array($emptyColumns)) {
diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php
index 8a9be015d7..d00ed25905 100644
--- a/plugins/Actions/Actions.php
+++ b/plugins/Actions/Actions.php
@@ -564,20 +564,6 @@ class Piwik_Actions extends Piwik_Plugin
/**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod($notification)
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite);
- return $actionsArchiving->archivePeriod($archiveProcessing);
- }
-
- /**
* Compute all the actions along with their hierarchies.
*
* For each action we process the "interest statistics" :
@@ -587,13 +573,23 @@ class Piwik_Actions extends Piwik_Plugin
*/
public function archiveDay($notification)
{
- /* @var $archiveProcessing Piwik_ArchiveProcessing_Day */
- $archiveProcessing = $notification->getNotificationObject();
+ /* @var $archiveProcessor Piwik_ArchiveProcessor_Day */
+ $archiveProcessor = $notification->getNotificationObject();
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+ $archiving = new Piwik_Actions_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archiveDay();
+ }
+ }
- $actionsArchiving = new Piwik_Actions_Archiving($archiveProcessing->idsite);
- return $actionsArchiving->archiveDay($archiveProcessing);
+ function archivePeriod($notification)
+ {
+ $archiveProcessor = $notification->getNotificationObject();
+
+ $archiving = new Piwik_Actions_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
+ }
}
static public function checkCustomVariablesPluginEnabled()
diff --git a/plugins/Actions/Archiving.php b/plugins/Actions/Archiver.php
index fa514b6ef9..b677e29eec 100644
--- a/plugins/Actions/Archiving.php
+++ b/plugins/Actions/Archiver.php
@@ -14,9 +14,36 @@
*
* @package Piwik_Actions
*/
-class Piwik_Actions_Archiving
+class Piwik_Actions_Archiver extends Piwik_PluginsArchiver
{
- protected $actionsTablesByType = null;
+ const DOWNLOADS_RECORD_NAME = 'Actions_downloads';
+ const OUTLINKS_RECORD_NAME = 'Actions_outlink';
+ const PAGE_TITLES_RECORD_NAME = 'Actions_actions';
+ const SITE_SEARCH_RECORD_NAME = 'Actions_sitesearch';
+ const PAGE_URLS_RECORD_NAME = 'Actions_actions_url';
+
+ const METRIC_PAGEVIEWS_RECORD_NAME = 'Actions_nb_pageviews';
+ const METRIC_UNIQ_PAGEVIEWS_RECORD_NAME = 'Actions_nb_uniq_pageviews';
+ const METRIC_SUM_TIME_RECORD_NAME = 'Actions_sum_time_generation';
+ const METRIC_HITS_TIMED_RECORD_NAME = 'Actions_nb_hits_with_time_generation';
+ const METRIC_DOWNLOADS_RECORD_NAME = 'Actions_nb_downloads';
+ const METRIC_UNIQ_DOWNLOADS_RECORD_NAME = 'Actions_nb_uniq_downloads';
+ const METRIC_OUTLINKS_RECORD_NAME = 'Actions_nb_outlinks';
+ const METRIC_UNIQ_OUTLINKS_RECORD_NAME = 'Actions_nb_uniq_outlinks';
+ const METRIC_SEARCHES_RECORD_NAME = 'Actions_nb_searches';
+ const METRIC_KEYWORDS_RECORD_NAME = 'Actions_nb_keywords';
+
+ /* Metrics in use by the API Actions.get */
+ public static $actionsAggregateMetrics = array(
+ self::METRIC_PAGEVIEWS_RECORD_NAME => 'nb_pageviews',
+ self::METRIC_UNIQ_PAGEVIEWS_RECORD_NAME => 'nb_uniq_pageviews',
+ self::METRIC_DOWNLOADS_RECORD_NAME => 'nb_downloads',
+ self::METRIC_UNIQ_DOWNLOADS_RECORD_NAME => 'nb_uniq_downloads',
+ self::METRIC_OUTLINKS_RECORD_NAME => 'nb_outlinks',
+ self::METRIC_UNIQ_OUTLINKS_RECORD_NAME => 'nb_uniq_outlinks',
+ self::METRIC_SEARCHES_RECORD_NAME => 'nb_searches',
+ self::METRIC_KEYWORDS_RECORD_NAME => 'nb_keywords',
+ );
public static $actionTypes = array(
Piwik_Tracker_Action::TYPE_ACTION_URL,
@@ -25,88 +52,39 @@ class Piwik_Actions_Archiving
Piwik_Tracker_Action::TYPE_ACTION_NAME,
Piwik_Tracker_Action::TYPE_SITE_SEARCH,
);
-
- private static $actionColumnAggregationOperations = array(
- Piwik_Archive::INDEX_PAGE_MAX_TIME_GENERATION => 'max',
- Piwik_Archive::INDEX_PAGE_MIN_TIME_GENERATION => 'min'
- );
-
static protected $invalidSummedColumnNameToRenamedNameFromPeriodArchive = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => Piwik_Archive::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS,
+ Piwik_Metrics::INDEX_NB_UNIQ_VISITORS => Piwik_Metrics::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => Piwik_Metrics::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS,
+ Piwik_Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => Piwik_Metrics::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS,
);
-
static protected $invalidSummedColumnNameToDeleteFromDayArchive = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
+ Piwik_Metrics::INDEX_NB_UNIQ_VISITORS,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
+ Piwik_Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
);
-
+ private static $actionColumnAggregationOperations = array(
+ Piwik_Metrics::INDEX_PAGE_MAX_TIME_GENERATION => 'max',
+ Piwik_Metrics::INDEX_PAGE_MIN_TIME_GENERATION => 'min'
+ );
+ protected $actionsTablesByType = null;
protected $isSiteSearchEnabled = false;
- function __construct($idSite)
+ function __construct($processor)
{
- $this->isSiteSearchEnabled = Piwik_Site::isSiteSearchEnabledFor($idSite);
- }
-
- /**
- * Archives Actions reports for a Period
- * @param Piwik_ArchiveProcessing_Period $archiveProcessing
- * @return bool
- */
- public function archivePeriod(Piwik_ArchiveProcessing_Period $archiveProcessing)
- {
- Piwik_Actions_ArchivingHelper::reloadConfig();
- $dataTableToSum = array(
- 'Actions_actions',
- 'Actions_actions_url',
- );
- $archiveProcessing->archiveDataTable($dataTableToSum,
- self::$invalidSummedColumnNameToRenamedNameFromPeriodArchive,
- Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero,
- Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable,
- Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation,
- self::$actionColumnAggregationOperations);
-
- $dataTableToSum = array(
- 'Actions_downloads',
- 'Actions_outlink',
- 'Actions_sitesearch',
- );
- $nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum,
- self::$invalidSummedColumnNameToRenamedNameFromPeriodArchive,
- Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero,
- Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable,
- Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
-
- $archiveProcessing->archiveNumericValuesSum(array(
- 'Actions_nb_pageviews',
- 'Actions_nb_uniq_pageviews',
- 'Actions_nb_downloads',
- 'Actions_nb_uniq_downloads',
- 'Actions_nb_outlinks',
- 'Actions_nb_uniq_outlinks',
- 'Actions_nb_searches',
- 'Actions_sum_time_generation',
- 'Actions_nb_hits_with_time_generation',
- ));
-
- // Unique Keywords can't be summed, instead we take the RowsCount() of the keyword table
- $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $nameToCount['Actions_sitesearch']['level0']);
- return true;
+ parent::__construct($processor);
+ $this->isSiteSearchEnabled = $processor->getSite()->isSiteSearchEnabled();
}
/**
* Archives Actions reports for a Day
*
- * @param Piwik_ArchiveProcessing $archiveProcessing
+ * @param Piwik_ArchiveProcessor $this->getProcessor()
* @return bool
*/
- public function archiveDay(Piwik_ArchiveProcessing $archiveProcessing)
+ public function archiveDay()
{
$rankingQueryLimit = self::getRankingQueryLimit();
-
+
// FIXME: This is a quick fix for #3482. The actual cause of the bug is that
// the site search & performance metrics additions to
// Piwik_Actions_ArchivingHelper::updateActionsTableWithRowQuery expect every
@@ -132,49 +110,86 @@ class Piwik_Actions_Archiving
if ($rankingQueryLimit === 0) {
$rankingQueryLimit = 100000;
}
-
+
Piwik_Actions_ArchivingHelper::reloadConfig();
$this->initActionsTables();
- $this->archiveDayActions($archiveProcessing, $rankingQueryLimit);
- $this->archiveDayEntryActions($archiveProcessing, $rankingQueryLimit);
- $this->archiveDayExitActions($archiveProcessing, $rankingQueryLimit);
- $this->archiveDayActionsTime($archiveProcessing, $rankingQueryLimit);
+ $this->archiveDayActions($rankingQueryLimit);
+ $this->archiveDayEntryActions($rankingQueryLimit);
+ $this->archiveDayExitActions($rankingQueryLimit);
+ $this->archiveDayActionsTime($rankingQueryLimit);
- // Record the final datasets
- $this->archiveDayRecordInDatabase($archiveProcessing);
+ $this->recordDayReports();
return true;
}
+ /**
+ * Returns the limit to use with RankingQuery for this plugin.
+ *
+ * @return int
+ */
+ private static function getRankingQueryLimit()
+ {
+ $configGeneral = Piwik_Config::getInstance()->General;
+ $configLimit = $configGeneral['archiving_ranking_query_row_limit'];
+ return $configLimit == 0 ? 0 : max(
+ $configLimit,
+ $configGeneral['datatable_archiving_maximum_rows_actions'],
+ $configGeneral['datatable_archiving_maximum_rows_subtable_actions']
+ );
+ }
+
/*
* Page URLs and Page names, general stats
*/
- protected function archiveDayActions($archiveProcessing, $rankingQueryLimit)
+
+ /**
+ * Initializes the DataTables created by the archiveDay function.
+ */
+ private function initActionsTables()
+ {
+ $this->actionsTablesByType = array();
+ foreach (self::$actionTypes as $type) {
+ $dataTable = new Piwik_DataTable();
+ $dataTable->setMaximumAllowedRows(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero);
+
+ if ($type == Piwik_Tracker_Action::TYPE_ACTION_URL
+ || $type == Piwik_Tracker_Action::TYPE_ACTION_NAME
+ ) {
+ // for page urls and page titles, performance metrics exist and have to be aggregated correctly
+ $dataTable->setColumnAggregationOperations(self::$actionColumnAggregationOperations);
+ }
+
+ $this->actionsTablesByType[$type] = $dataTable;
+ }
+ }
+
+ protected function archiveDayActions($rankingQueryLimit)
{
$select = "log_action.name,
log_action.type,
log_action.idaction,
log_action.url_prefix,
- count(distinct log_link_visit_action.idvisit) as `" . Piwik_Archive::INDEX_NB_VISITS . "`,
- count(distinct log_link_visit_action.idvisitor) as `" . Piwik_Archive::INDEX_NB_UNIQ_VISITORS . "`,
- count(*) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS . "`,
+ count(distinct log_link_visit_action.idvisit) as `" . Piwik_Metrics::INDEX_NB_VISITS . "`,
+ count(distinct log_link_visit_action.idvisitor) as `" . Piwik_Metrics::INDEX_NB_UNIQ_VISITORS . "`,
+ count(*) as `" . Piwik_Metrics::INDEX_PAGE_NB_HITS . "`,
sum(
case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . " is null
- then 0
+ then 0
else " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . "
end
- ) / 1000 as `" . Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION . "`,
+ ) / 1000 as `" . Piwik_Metrics::INDEX_PAGE_SUM_TIME_GENERATION . "`,
sum(
case when " . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . " is null
then 0
else 1
end
- ) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION . "`,
+ ) as `" . Piwik_Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION . "`,
min(" . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . ") / 1000
- as `" . Piwik_Archive::INDEX_PAGE_MIN_TIME_GENERATION . "`,
+ as `" . Piwik_Metrics::INDEX_PAGE_MIN_TIME_GENERATION . "`,
max(" . Piwik_Tracker_Action::DB_COLUMN_TIME_GENERATION . ") / 1000
- as `" . Piwik_Archive::INDEX_PAGE_MAX_TIME_GENERATION . "`
+ as `" . Piwik_Metrics::INDEX_PAGE_MAX_TIME_GENERATION . "`
";
$from = array(
@@ -191,23 +206,23 @@ class Piwik_Actions_Archiving
AND log_link_visit_action.%s IS NOT NULL";
$groupBy = "log_action.idaction";
- $orderBy = "`" . Piwik_Archive::INDEX_PAGE_NB_HITS . "` DESC, name ASC";
+ $orderBy = "`" . Piwik_Metrics::INDEX_PAGE_NB_HITS . "` DESC, name ASC";
$rankingQuery = false;
if ($rankingQueryLimit > 0) {
$rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
$rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
$rankingQuery->addLabelColumn(array('idaction', 'name'));
- $rankingQuery->addColumn(array('url_prefix', Piwik_Archive::INDEX_NB_UNIQ_VISITORS));
- $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_NB_HITS, Piwik_Archive::INDEX_NB_VISITS), 'sum');
+ $rankingQuery->addColumn(array('url_prefix', Piwik_Metrics::INDEX_NB_UNIQ_VISITORS));
+ $rankingQuery->addColumn(array(Piwik_Metrics::INDEX_PAGE_NB_HITS, Piwik_Metrics::INDEX_NB_VISITS), 'sum');
if ($this->isSiteSearchEnabled()) {
- $rankingQuery->addColumn(Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, 'sum');
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min');
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, 'sum');
}
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, 'sum');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, 'sum');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_MIN_TIME_GENERATION, 'min');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_MAX_TIME_GENERATION, 'max');
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_PAGE_SUM_TIME_GENERATION, 'sum');
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, 'sum');
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_PAGE_MIN_TIME_GENERATION, 'min');
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_PAGE_MAX_TIME_GENERATION, 'max');
$rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
}
@@ -216,7 +231,7 @@ class Piwik_Actions_Archiving
// 2) For each page view, count number of times the referrer page was a Site Search
if ($this->isSiteSearchEnabled()) {
$selectFlagNoResultKeywords = ",
- CASE WHEN (MAX(log_link_visit_action.custom_var_v" . Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT . ") = 0 AND log_link_visit_action.custom_var_k" . Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT . " = '" . Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT . "') THEN 1 ELSE 0 END AS `" . Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`";
+ CASE WHEN (MAX(log_link_visit_action.custom_var_v" . Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT . ") = 0 AND log_link_visit_action.custom_var_k" . Piwik_Tracker_Action::CVAR_INDEX_SEARCH_COUNT . " = '" . Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT . "') THEN 1 ELSE 0 END AS `" . Piwik_Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`";
//we need an extra JOIN to know whether the referrer "idaction_name_ref" was a Site Search request
$from[] = array(
@@ -226,39 +241,62 @@ class Piwik_Actions_Archiving
);
$selectSiteSearchFollowingPages = ",
- SUM(CASE WHEN log_action_name_ref.type = " . Piwik_Tracker_Action::TYPE_SITE_SEARCH . " THEN 1 ELSE 0 END) AS `" . Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS . "`";
+ SUM(CASE WHEN log_action_name_ref.type = " . Piwik_Tracker_Action::TYPE_SITE_SEARCH . " THEN 1 ELSE 0 END) AS `" . Piwik_Metrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS . "`";
$select .= $selectFlagNoResultKeywords
. $selectSiteSearchFollowingPages;
- // Not working yet
-// $selectRefPageIsStartingSiteSearch = ",
-// SUM(CASE WHEN log_action_name_ref.type = " . Piwik_Tracker_Action::TYPE_ACTION_NAME . " THEN 1 ELSE 0 END) AS `". Piwik_Archive::INDEX_PAGE_STARTING_SITE_SEARCH_NB_HITS."`";
-// . $selectRefPageIsStartingSiteSearch
-// . ", idaction_url_ref, idaction_name_ref"
}
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_name", $archiveProcessing, $rankingQuery);
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_name", $rankingQuery);
+
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_url", $rankingQuery);
+ }
+
+ protected function isSiteSearchEnabled()
+ {
+ return $this->isSiteSearchEnabled;
+ }
+
+ protected function archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, $sprintfField, $rankingQuery = false)
+ {
+ // idaction field needs to be set in select clause before calling getSelectQuery().
+ // if a complex segmentation join is needed, the field needs to be propagated
+ // to the outer select. therefore, $segment needs to know about it.
+ $select = sprintf($select, $sprintfField);
+
+
+ // get query with segmentation
+ $query = $this->getLogAggregator()->generateQuery($select, $from, $where, $groupBy, $orderBy);
+
+ // replace the rest of the %s
+ $querySql = str_replace("%s", $sprintfField, $query['sql']);
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_url", $archiveProcessing, $rankingQuery);
+ // apply ranking query
+ if ($rankingQuery) {
+ $querySql = $rankingQuery->generateQuery($querySql);
+ }
+
+ // get result
+ $resultSet = $this->getLogAggregator()->getDb()->query($querySql, $query['bind']);
+ $modified = Piwik_Actions_ArchivingHelper::updateActionsTableWithRowQuery($resultSet, $sprintfField, $this->actionsTablesByType);
+ return $modified;
}
/**
* Entry actions for Page URLs and Page names
*/
- protected function archiveDayEntryActions($archiveProcessing, $rankingQueryLimit)
+ protected function archiveDayEntryActions($rankingQueryLimit)
{
$rankingQuery = false;
if ($rankingQueryLimit > 0) {
$rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
$rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
$rankingQuery->addLabelColumn('idaction');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS);
- $rankingQuery->addColumn(array(Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS,
- Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
- Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT), 'sum');
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS);
+ $rankingQuery->addColumn(array(Piwik_Metrics::INDEX_PAGE_ENTRY_NB_VISITS,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_NB_ACTIONS,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT), 'sum');
$rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
$extraSelects = 'log_action.type, log_action.name,';
@@ -269,7 +307,7 @@ class Piwik_Actions_Archiving
"joinOn" => "log_visit.%s = log_action.idaction"
)
);
- $orderBy = "`" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS . "` DESC, log_action.name ASC";
+ $orderBy = "`" . Piwik_Metrics::INDEX_PAGE_ENTRY_NB_ACTIONS . "` DESC, log_action.name ASC";
} else {
$extraSelects = false;
$from = "log_visit";
@@ -277,11 +315,11 @@ class Piwik_Actions_Archiving
}
$select = "log_visit.%s as idaction, $extraSelects
- count(distinct log_visit.idvisitor) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS . "`,
- count(*) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS . "`,
- sum(log_visit.visit_total_actions) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS . "`,
- sum(log_visit.visit_total_time) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH . "`,
- sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `" . Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT . "`";
+ count(distinct log_visit.idvisitor) as `" . Piwik_Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS . "`,
+ count(*) as `" . Piwik_Metrics::INDEX_PAGE_ENTRY_NB_VISITS . "`,
+ sum(log_visit.visit_total_actions) as `" . Piwik_Metrics::INDEX_PAGE_ENTRY_NB_ACTIONS . "`,
+ sum(log_visit.visit_total_time) as `" . Piwik_Metrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH . "`,
+ sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `" . Piwik_Metrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT . "`";
$where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
@@ -290,254 +328,145 @@ class Piwik_Actions_Archiving
$groupBy = "log_visit.%s, idaction";
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_entry_idaction_url", $archiveProcessing, $rankingQuery);
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "visit_entry_idaction_url", $rankingQuery);
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_entry_idaction_name", $archiveProcessing, $rankingQuery);
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "visit_entry_idaction_name", $rankingQuery);
}
-
/**
- * Time per action
+ * Exit actions
*/
- protected function archiveDayActionsTime($archiveProcessing, $rankingQueryLimit)
+ public function archiveDayExitActions($rankingQueryLimit)
{
$rankingQuery = false;
if ($rankingQueryLimit > 0) {
$rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
$rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
$rankingQuery->addLabelColumn('idaction');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT, 'sum');
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS);
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_PAGE_EXIT_NB_VISITS, 'sum');
$rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
- $extraSelects = "log_action.type, log_action.name, count(*) as `" . Piwik_Archive::INDEX_PAGE_NB_HITS . "`,";
+ $extraSelects = 'log_action.type, log_action.name,';
$from = array(
- "log_link_visit_action",
+ "log_visit",
array(
"table" => "log_action",
- "joinOn" => "log_link_visit_action.%s = log_action.idaction"
+ "joinOn" => "log_visit.%s = log_action.idaction"
)
);
- $orderBy = "`" . Piwik_Archive::INDEX_PAGE_NB_HITS . "` DESC, log_action.name ASC";
+ $orderBy = "`" . Piwik_Metrics::INDEX_PAGE_EXIT_NB_VISITS . "` DESC, log_action.name ASC";
} else {
$extraSelects = false;
- $from = "log_link_visit_action";
+ $from = "log_visit";
$orderBy = false;
}
- $select = "log_link_visit_action.%s as idaction, $extraSelects
- sum(log_link_visit_action.time_spent_ref_action) as `" . Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT . "`";
+ $select = "log_visit.%s as idaction, $extraSelects
+ count(distinct log_visit.idvisitor) as `" . Piwik_Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS . "`,
+ count(*) as `" . Piwik_Metrics::INDEX_PAGE_EXIT_NB_VISITS . "`";
- $where = "log_link_visit_action.server_time >= ?
- AND log_link_visit_action.server_time <= ?
- AND log_link_visit_action.idsite = ?
- AND log_link_visit_action.time_spent_ref_action > 0
- AND log_link_visit_action.%s > 0";
+ $where = "log_visit.visit_last_action_time >= ?
+ AND log_visit.visit_last_action_time <= ?
+ AND log_visit.idsite = ?
+ AND log_visit.%s > 0";
- $groupBy = "log_link_visit_action.%s, idaction";
+ $groupBy = "log_visit.%s, idaction";
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_url_ref", $archiveProcessing, $rankingQuery);
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "visit_exit_idaction_url", $rankingQuery);
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "idaction_name_ref", $archiveProcessing, $rankingQuery);
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "visit_exit_idaction_name", $rankingQuery);
+ return array($rankingQuery, $extraSelects, $from, $orderBy, $select, $where, $groupBy);
}
/**
- * Exit actions
+ * Time per action
*/
- public function archiveDayExitActions($archiveProcessing, $rankingQueryLimit)
+ protected function archiveDayActionsTime($rankingQueryLimit)
{
$rankingQuery = false;
if ($rankingQueryLimit > 0) {
$rankingQuery = new Piwik_RankingQuery($rankingQueryLimit);
$rankingQuery->setOthersLabel(Piwik_DataTable::LABEL_SUMMARY_ROW);
$rankingQuery->addLabelColumn('idaction');
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS);
- $rankingQuery->addColumn(Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS, 'sum');
+ $rankingQuery->addColumn(Piwik_Metrics::INDEX_PAGE_SUM_TIME_SPENT, 'sum');
$rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
- $extraSelects = 'log_action.type, log_action.name,';
+ $extraSelects = "log_action.type, log_action.name, count(*) as `" . Piwik_Metrics::INDEX_PAGE_NB_HITS . "`,";
$from = array(
- "log_visit",
+ "log_link_visit_action",
array(
"table" => "log_action",
- "joinOn" => "log_visit.%s = log_action.idaction"
+ "joinOn" => "log_link_visit_action.%s = log_action.idaction"
)
);
- $orderBy = "`" . Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS . "` DESC, log_action.name ASC";
+ $orderBy = "`" . Piwik_Metrics::INDEX_PAGE_NB_HITS . "` DESC, log_action.name ASC";
} else {
$extraSelects = false;
- $from = "log_visit";
+ $from = "log_link_visit_action";
$orderBy = false;
}
- $select = "log_visit.%s as idaction, $extraSelects
- count(distinct log_visit.idvisitor) as `" . Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS . "`,
- count(*) as `" . Piwik_Archive::INDEX_PAGE_EXIT_NB_VISITS . "`";
+ $select = "log_link_visit_action.%s as idaction, $extraSelects
+ sum(log_link_visit_action.time_spent_ref_action) as `" . Piwik_Metrics::INDEX_PAGE_SUM_TIME_SPENT . "`";
- $where = "log_visit.visit_last_action_time >= ?
- AND log_visit.visit_last_action_time <= ?
- AND log_visit.idsite = ?
- AND log_visit.%s > 0";
+ $where = "log_link_visit_action.server_time >= ?
+ AND log_link_visit_action.server_time <= ?
+ AND log_link_visit_action.idsite = ?
+ AND log_link_visit_action.time_spent_ref_action > 0
+ AND log_link_visit_action.%s > 0";
- $groupBy = "log_visit.%s, idaction";
+ $groupBy = "log_link_visit_action.%s, idaction";
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_exit_idaction_url", $archiveProcessing, $rankingQuery);
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_url_ref", $rankingQuery);
- $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- "visit_exit_idaction_name", $archiveProcessing, $rankingQuery);
- return array($rankingQuery, $extraSelects, $from, $orderBy, $select, $where, $groupBy);
+ $this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_name_ref", $rankingQuery);
}
-
/**
* Records in the DB the archived reports for Page views, Downloads, Outlinks, and Page titles
- *
- * @param $archiveProcessing
*/
- protected function archiveDayRecordInDatabase($archiveProcessing)
+ protected function recordDayReports()
{
Piwik_Actions_ArchivingHelper::clearActionsCache();
- /** @var Piwik_DataTable $dataTable */
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_URL];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
- $archiveProcessing->insertBlobRecord('Actions_actions_url', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_uniq_pageviews', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- $archiveProcessing->insertNumericRecord('Actions_sum_time_generation', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION)));
- $archiveProcessing->insertNumericRecord('Actions_nb_hits_with_time_generation', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION)));
- destroy($dataTable);
-
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_DOWNLOAD];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
- $archiveProcessing->insertBlobRecord('Actions_downloads', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_uniq_downloads', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- destroy($dataTable);
-
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_OUTLINK];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
- $archiveProcessing->insertBlobRecord('Actions_outlink', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_uniq_outlinks', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- destroy($dataTable);
-
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_NAME];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
- $archiveProcessing->insertBlobRecord('Actions_actions', $s);
- destroy($dataTable);
-
- $dataTable = $this->actionsTablesByType[Piwik_Tracker_Action::TYPE_SITE_SEARCH];
- self::deleteInvalidSummedColumnsFromDataTable($dataTable);
- $this->deleteUnusedColumnsFromKeywordsDataTable($dataTable);
- $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
- $archiveProcessing->insertBlobRecord('Actions_sitesearch', $s);
- $archiveProcessing->insertNumericRecord('Actions_nb_searches', array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS)));
- $archiveProcessing->insertNumericRecord('Actions_nb_keywords', $dataTable->getRowsCount());
- destroy($dataTable);
-
- destroy($this->actionsTablesByType);
+ $this->recordPageUrlsReports();
+ $this->recordDownloadsReports();
+ $this->recordOutlinksReports();
+ $this->recordPageTitlesReports();
+ $this->recordSiteSearchReports();
}
- protected function deleteUnusedColumnsFromKeywordsDataTable($dataTable)
+ protected function recordPageUrlsReports()
{
- $columnsToDelete = array(
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_ACTIONS,
- Piwik_Archive::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
- Piwik_Archive::INDEX_PAGE_ENTRY_NB_VISITS,
- Piwik_Archive::INDEX_PAGE_ENTRY_BOUNCE_COUNT,
- Piwik_Archive::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
+ $dataTable = $this->getDataTable(Piwik_Tracker_Action::TYPE_ACTION_URL);
+ $this->recordDataTable($dataTable, self::PAGE_URLS_RECORD_NAME);
+
+ $records = array(
+ self::METRIC_PAGEVIEWS_RECORD_NAME => array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_PAGE_NB_HITS)),
+ self::METRIC_UNIQ_PAGEVIEWS_RECORD_NAME => array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_NB_VISITS)),
+ self::METRIC_SUM_TIME_RECORD_NAME => array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_PAGE_SUM_TIME_GENERATION)),
+ self::METRIC_HITS_TIMED_RECORD_NAME => array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION))
);
- $dataTable->deleteColumns($columnsToDelete);
- }
-
- static protected function removeEmptyColumns($dataTable)
- {
- // Delete all columns that have a value of zero
- $dataTable->filter('ColumnDelete', array(
- $columnsToRemove = array(Piwik_Archive::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS),
- $columnsToKeep = array(),
- $deleteIfZeroOnly = true
- ));
+ $this->getProcessor()->insertNumericRecords( $records );
}
-
/**
- * Returns the limit to use with RankingQuery for this plugin.
- *
- * @return int
+ * @param $typeId
+ * @return Piwik_DataTable
*/
- private static function getRankingQueryLimit()
+ protected function getDataTable($typeId)
{
- $configGeneral = Piwik_Config::getInstance()->General;
- $configLimit = $configGeneral['archiving_ranking_query_row_limit'];
- return $configLimit == 0 ? 0 : max(
- $configLimit,
- $configGeneral['datatable_archiving_maximum_rows_actions'],
- $configGeneral['datatable_archiving_maximum_rows_subtable_actions']
- );
+ return $this->actionsTablesByType[$typeId];
}
-
- /**
- * @param $select
- * @param $from
- * @param $where
- * @param $orderBy
- * @param $groupBy
- * @param $sprintfField
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @param Piwik_RankingQuery|false $rankingQuery
- * @return int
- */
- protected function archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy,
- $sprintfField, $archiveProcessing, $rankingQuery = false)
+ protected function recordDataTable($dataTable, $recordName)
{
- // idaction field needs to be set in select clause before calling getSelectQuery().
- // if a complex segmentation join is needed, the field needs to be propagated
- // to the outer select. therefore, $segment needs to know about it.
- $select = sprintf($select, $sprintfField);
-
- $bind = array();
-
- // get query with segmentation
- $query = $archiveProcessing->getSegment()->getSelectQuery(
- $select, $from, $where, $bind, $orderBy, $groupBy);
-
- // extend bindings
- $bind = array_merge(array($archiveProcessing->getStartDatetimeUTC(),
- $archiveProcessing->getEndDatetimeUTC(),
- $archiveProcessing->idsite
- ),
- $query['bind']
- );
-
- // replace the rest of the %s
- $querySql = str_replace("%s", $sprintfField, $query['sql']);
-
- // apply ranking query
- if ($rankingQuery) {
- $querySql = $rankingQuery->generateQuery($querySql);
- }
-
- // get result
- $resultSet = $archiveProcessing->db->query($querySql, $bind);
- $modified = Piwik_Actions_ArchivingHelper::updateActionsTableWithRowQuery($resultSet, $sprintfField, $this->actionsTablesByType);
- return $modified;
+ self::deleteInvalidSummedColumnsFromDataTable($dataTable);
+ $s = $dataTable->getSerialized(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero, Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable, Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation);
+ $this->getProcessor()->insertBlobRecord($recordName, $s);
}
-
/**
* For rows which have subtables (eg. directories with sub pages),
* deletes columns which don't make sense when all values of sub pages are summed.
@@ -569,28 +498,107 @@ class Piwik_Actions_Archiving
self::removeEmptyColumns($dataTable);
}
- /**
- * Initializes the DataTables created by the archiveDay function.
- */
- private function initActionsTables()
+ static protected function removeEmptyColumns($dataTable)
{
- $this->actionsTablesByType = array();
- foreach (self::$actionTypes as $type) {
- $dataTable = new Piwik_DataTable();
- $dataTable->setMaximumAllowedRows(Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero);
+ // Delete all columns that have a value of zero
+ $dataTable->filter('ColumnDelete', array(
+ $columnsToRemove = array(Piwik_Metrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS),
+ $columnsToKeep = array(),
+ $deleteIfZeroOnly = true
+ ));
+ }
- if ($type == Piwik_Tracker_Action::TYPE_ACTION_URL
- || $type == Piwik_Tracker_Action::TYPE_ACTION_NAME) {
- // for page urls and page titles, performance metrics exist and have to be aggregated correctly
- $dataTable->setColumnAggregationOperations(self::$actionColumnAggregationOperations);
- }
-
- $this->actionsTablesByType[$type] = $dataTable;
- }
+ protected function recordDownloadsReports()
+ {
+ $dataTable = $this->getDataTable(Piwik_Tracker_Action::TYPE_DOWNLOAD);
+ $this->recordDataTable($dataTable, self::DOWNLOADS_RECORD_NAME);
+
+ $this->getProcessor()->insertNumericRecord(self::METRIC_DOWNLOADS_RECORD_NAME, array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_PAGE_NB_HITS)));
+ $this->getProcessor()->insertNumericRecord(self::METRIC_UNIQ_DOWNLOADS_RECORD_NAME, array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_NB_VISITS)));
}
- protected function isSiteSearchEnabled()
+ protected function recordOutlinksReports()
{
- return $this->isSiteSearchEnabled;
+ $dataTable = $this->getDataTable(Piwik_Tracker_Action::TYPE_OUTLINK);
+ $this->recordDataTable($dataTable, self::OUTLINKS_RECORD_NAME);
+
+ $this->getProcessor()->insertNumericRecord(self::METRIC_OUTLINKS_RECORD_NAME, array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_PAGE_NB_HITS)));
+ $this->getProcessor()->insertNumericRecord(self::METRIC_UNIQ_OUTLINKS_RECORD_NAME, array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_NB_VISITS)));
+ }
+
+ protected function recordPageTitlesReports()
+ {
+ $dataTable = $this->getDataTable(Piwik_Tracker_Action::TYPE_ACTION_NAME);
+ $this->recordDataTable($dataTable, self::PAGE_TITLES_RECORD_NAME);
+ }
+
+ protected function recordSiteSearchReports()
+ {
+ $dataTable = $this->getDataTable(Piwik_Tracker_Action::TYPE_SITE_SEARCH);
+ $this->deleteUnusedColumnsFromKeywordsDataTable($dataTable);
+ $this->recordDataTable($dataTable, self::SITE_SEARCH_RECORD_NAME);
+
+ $this->getProcessor()->insertNumericRecord(self::METRIC_SEARCHES_RECORD_NAME, array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_NB_VISITS)));
+ $this->getProcessor()->insertNumericRecord(self::METRIC_KEYWORDS_RECORD_NAME, $dataTable->getRowsCount());
+ }
+
+ protected function deleteUnusedColumnsFromKeywordsDataTable($dataTable)
+ {
+ $columnsToDelete = array(
+ Piwik_Metrics::INDEX_NB_UNIQ_VISITORS,
+ Piwik_Metrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_NB_ACTIONS,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_NB_VISITS,
+ Piwik_Metrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT,
+ Piwik_Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
+ );
+ $dataTable->deleteColumns($columnsToDelete);
+ }
+
+ public function archivePeriod()
+ {
+ Piwik_Actions_ArchivingHelper::reloadConfig();
+ $dataTableToSum = array(
+ self::PAGE_TITLES_RECORD_NAME,
+ self::PAGE_URLS_RECORD_NAME,
+ );
+ $this->getProcessor()->aggregateDataTableReports($dataTableToSum,
+ Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero,
+ Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable,
+ Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation,
+ self::$actionColumnAggregationOperations,
+ self::$invalidSummedColumnNameToRenamedNameFromPeriodArchive
+ );
+
+ $dataTableToSum = array(
+ self::DOWNLOADS_RECORD_NAME,
+ self::OUTLINKS_RECORD_NAME,
+ self::SITE_SEARCH_RECORD_NAME,
+ );
+ $aggregation = null;
+ $nameToCount = $this->getProcessor()->aggregateDataTableReports($dataTableToSum,
+ Piwik_Actions_ArchivingHelper::$maximumRowsInDataTableLevelZero,
+ Piwik_Actions_ArchivingHelper::$maximumRowsInSubDataTable,
+ Piwik_Actions_ArchivingHelper::$columnToSortByBeforeTruncation,
+ $aggregation,
+ self::$invalidSummedColumnNameToRenamedNameFromPeriodArchive
+ );
+
+ $this->getProcessor()->aggregateNumericMetrics(array(
+ self::METRIC_PAGEVIEWS_RECORD_NAME,
+ self::METRIC_UNIQ_PAGEVIEWS_RECORD_NAME,
+ self::METRIC_DOWNLOADS_RECORD_NAME,
+ self::METRIC_UNIQ_DOWNLOADS_RECORD_NAME,
+ self::METRIC_OUTLINKS_RECORD_NAME,
+ self::METRIC_UNIQ_OUTLINKS_RECORD_NAME,
+ self::METRIC_SEARCHES_RECORD_NAME,
+ self::METRIC_SUM_TIME_RECORD_NAME,
+ self::METRIC_HITS_TIMED_RECORD_NAME,
+ ));
+
+ // Unique Keywords can't be summed, instead we take the RowsCount() of the keyword table
+ $this->getProcessor()->insertNumericRecord(self::METRIC_KEYWORDS_RECORD_NAME, $nameToCount[self::SITE_SEARCH_RECORD_NAME]['level0']);
}
}
diff --git a/plugins/Actions/ArchivingHelper.php b/plugins/Actions/ArchivingHelper.php
index e5773d96e6..0f7744522c 100644
--- a/plugins/Actions/ArchivingHelper.php
+++ b/plugins/Actions/ArchivingHelper.php
@@ -22,7 +22,7 @@ class Piwik_Actions_ArchivingHelper
const OTHERS_ROW_KEY = '';
/**
- * FIXME See FIXME related to this function at Piwik_Actions_Archiving::archiveDay.
+ * FIXME See FIXME related to this function at Piwik_Actions_Archiver::archiveDay.
*
* @param Zend_Db_Statement|PDOStatement $query
* @param string|bool $fieldQueried
@@ -42,7 +42,7 @@ class Piwik_Actions_ArchivingHelper
}
if ($row['type'] != Piwik_Tracker_Action::TYPE_SITE_SEARCH) {
- unset($row[Piwik_Archive::INDEX_SITE_SEARCH_HAS_NO_RESULT]);
+ unset($row[Piwik_Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT]);
}
// This will appear as <url /> in the API, which is actually very important to keep
@@ -100,15 +100,15 @@ class Piwik_Actions_ArchivingHelper
&& !$actionRow->isSummaryRow()
) {
if (($existingUrl = $actionRow->getMetadata('url')) !== false) {
- if (!empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS])
- && $row[Piwik_Archive::INDEX_PAGE_NB_HITS] > $actionRow->maxVisitsSummed
+ if (!empty($row[Piwik_Metrics::INDEX_PAGE_NB_HITS])
+ && $row[Piwik_Metrics::INDEX_PAGE_NB_HITS] > $actionRow->maxVisitsSummed
) {
$actionRow->setMetadata('url', $url);
- $actionRow->maxVisitsSummed = $row[Piwik_Archive::INDEX_PAGE_NB_HITS];
+ $actionRow->maxVisitsSummed = $row[Piwik_Metrics::INDEX_PAGE_NB_HITS];
}
} else {
$actionRow->setMetadata('url', $url);
- $actionRow->maxVisitsSummed = !empty($row[Piwik_Archive::INDEX_PAGE_NB_HITS]) ? $row[Piwik_Archive::INDEX_PAGE_NB_HITS] : 0;
+ $actionRow->maxVisitsSummed = !empty($row[Piwik_Metrics::INDEX_PAGE_NB_HITS]) ? $row[Piwik_Metrics::INDEX_PAGE_NB_HITS] : 0;
}
}
@@ -116,17 +116,17 @@ class Piwik_Actions_ArchivingHelper
&& $row['type'] != Piwik_Tracker_Action::TYPE_ACTION_NAME
) {
// only keep performance metrics when they're used (i.e. for URLs and page titles)
- if (array_key_exists(Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION, $row)) {
- unset($row[Piwik_Archive::INDEX_PAGE_SUM_TIME_GENERATION]);
+ if (array_key_exists(Piwik_Metrics::INDEX_PAGE_SUM_TIME_GENERATION, $row)) {
+ unset($row[Piwik_Metrics::INDEX_PAGE_SUM_TIME_GENERATION]);
}
- if (array_key_exists(Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, $row)) {
- unset($row[Piwik_Archive::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION]);
+ if (array_key_exists(Piwik_Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, $row)) {
+ unset($row[Piwik_Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION]);
}
- if (array_key_exists(Piwik_Archive::INDEX_PAGE_MIN_TIME_GENERATION, $row)) {
- unset($row[Piwik_Archive::INDEX_PAGE_MIN_TIME_GENERATION]);
+ if (array_key_exists(Piwik_Metrics::INDEX_PAGE_MIN_TIME_GENERATION, $row)) {
+ unset($row[Piwik_Metrics::INDEX_PAGE_MIN_TIME_GENERATION]);
}
- if (array_key_exists(Piwik_Archive::INDEX_PAGE_MAX_TIME_GENERATION, $row)) {
- unset($row[Piwik_Archive::INDEX_PAGE_MAX_TIME_GENERATION]);
+ if (array_key_exists(Piwik_Metrics::INDEX_PAGE_MAX_TIME_GENERATION, $row)) {
+ unset($row[Piwik_Metrics::INDEX_PAGE_MAX_TIME_GENERATION]);
}
}
@@ -150,7 +150,7 @@ class Piwik_Actions_ArchivingHelper
// if the exit_action was not recorded properly in the log_link_visit_action
// there would be an error message when getting the nb_hits column
// we must fake the record and add the columns
- if ($actionRow->getColumn(Piwik_Archive::INDEX_PAGE_NB_HITS) === false) {
+ if ($actionRow->getColumn(Piwik_Metrics::INDEX_PAGE_NB_HITS) === false) {
// to test this code: delete the entries in log_link_action_visit for
// a given exit_idaction_url
foreach (self::getDefaultRow()->getColumns() as $name => $value) {
@@ -173,7 +173,7 @@ class Piwik_Actions_ArchivingHelper
*/
private static function getColumnValuesMerged($columnName, $alreadyValue, $value)
{
- if ($columnName == Piwik_Archive::INDEX_PAGE_MIN_TIME_GENERATION) {
+ if ($columnName == Piwik_Metrics::INDEX_PAGE_MIN_TIME_GENERATION) {
if (empty($alreadyValue)) {
$newValue = $value;
} else if (empty($value)) {
@@ -183,7 +183,7 @@ class Piwik_Actions_ArchivingHelper
}
return $newValue;
}
- if ($columnName == Piwik_Archive::INDEX_PAGE_MAX_TIME_GENERATION) {
+ if ($columnName == Piwik_Metrics::INDEX_PAGE_MAX_TIME_GENERATION) {
$newValue = max($alreadyValue, $value);
return $newValue;
}
@@ -214,7 +214,7 @@ class Piwik_Actions_ArchivingHelper
}
self::$defaultActionName = Piwik_Config::getInstance()->General['action_default_name'];
- self::$columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
+ self::$columnToSortByBeforeTruncation = Piwik_Metrics::INDEX_NB_VISITS;
self::$maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_actions'];
self::$maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_actions'];
@@ -238,9 +238,9 @@ class Piwik_Actions_ArchivingHelper
// so we add this fake row information to make sure there is a nb_hits, etc. column for every action
$row = new Piwik_DataTable_Row(array(
Piwik_DataTable_Row::COLUMNS => array(
- Piwik_Archive::INDEX_NB_VISITS => 1,
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 1,
- Piwik_Archive::INDEX_PAGE_NB_HITS => 1,
+ Piwik_Metrics::INDEX_NB_VISITS => 1,
+ Piwik_Metrics::INDEX_NB_UNIQ_VISITORS => 1,
+ Piwik_Metrics::INDEX_PAGE_NB_HITS => 1,
)));
}
return $row;
@@ -485,10 +485,10 @@ class Piwik_Actions_ArchivingHelper
*/
private static function getDefaultRowColumns()
{
- return array(Piwik_Archive::INDEX_NB_VISITS => 0,
- Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0,
- Piwik_Archive::INDEX_PAGE_NB_HITS => 0,
- Piwik_Archive::INDEX_PAGE_SUM_TIME_SPENT => 0);
+ return array(Piwik_Metrics::INDEX_NB_VISITS => 0,
+ Piwik_Metrics::INDEX_NB_UNIQ_VISITORS => 0,
+ Piwik_Metrics::INDEX_PAGE_NB_HITS => 0,
+ Piwik_Metrics::INDEX_PAGE_SUM_TIME_SPENT => 0);
}
/**
diff --git a/plugins/Annotations/javascripts/annotations.js b/plugins/Annotations/javascripts/annotations.js
index 9f1070b8a2..25718fbbb0 100755
--- a/plugins/Annotations/javascripts/annotations.js
+++ b/plugins/Annotations/javascripts/annotations.js
@@ -45,7 +45,6 @@
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(ajaxParams, 'get');
- ajaxRequest.addParams({token_auth: piwik.token_auth}, 'post');
ajaxRequest.setCallback(callback);
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
@@ -68,7 +67,6 @@
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(ajaxParams, 'get');
- ajaxRequest.addParams({token_auth: piwik.token_auth}, 'post');
ajaxRequest.setCallback(callback);
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
@@ -88,7 +86,6 @@
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(ajaxParams, 'get');
- ajaxRequest.addParams({token_auth: piwik.token_auth}, 'post');
ajaxRequest.setCallback(callback);
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
@@ -211,7 +208,7 @@
* @return {boolean}
*/
var isAnnotationStarred = function (annotation) {
- return +$('.annotation-star', annotation).attr('data-starred') == 1 ? true : false;
+ return !!(+$('.annotation-star', annotation).attr('data-starred') == 1);
};
/**
diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php
index eded6d451f..e17fb28467 100644
--- a/plugins/CoreAdminHome/API.php
+++ b/plugins/CoreAdminHome/API.php
@@ -92,9 +92,6 @@ class Piwik_CoreAdminHome_API
}
}
- // Lookup archive tables
- $tables = Piwik::getTablesInstalled();
- $archiveTables = Piwik::getTablesArchivesInstalled();
// If using the feature "Delete logs older than N days"...
$logsAreDeletedBeforeThisDate = Piwik_Config::getInstance()->Deletelogs['delete_logs_schedule_lowest_interval'];
@@ -144,10 +141,10 @@ class Piwik_CoreAdminHome_API
// In each table, invalidate day/week/month/year containing this date
$sqlIdSites = implode(",", $idSites);
+ $archiveTables = Piwik_DataAccess_ArchiveTableCreator::getTablesArchivesInstalled();
foreach ($archiveTables as $table) {
// Extract Y_m from table name
- $suffix = str_replace(array('archive_numeric_', 'archive_blob_'), '', Piwik_Common::unprefixTable($table));
-
+ $suffix = Piwik_DataAccess_ArchiveTableCreator::getDateFromTableName($table);
if (!isset($datesByMonth[$suffix])) {
continue;
}
diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php
index cb914716cf..d9bf99b606 100644
--- a/plugins/CoreAdminHome/Controller.php
+++ b/plugins/CoreAdminHome/Controller.php
@@ -29,8 +29,8 @@ class Piwik_CoreAdminHome_Controller extends Piwik_Controller_Admin
$view = new Piwik_View('@CoreAdminHome/generalSettings');
if (Piwik::isUserIsSuperUser()) {
- $enableBrowserTriggerArchiving = Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled();
- $todayArchiveTimeToLive = Piwik_ArchiveProcessing::getTodayArchiveTimeToLive();
+ $enableBrowserTriggerArchiving = Piwik_ArchiveProcessor_Rules::isBrowserTriggerEnabled();
+ $todayArchiveTimeToLive = Piwik_ArchiveProcessor_Rules::getTodayArchiveTimeToLive();
$showWarningCron = false;
if (!$enableBrowserTriggerArchiving
&& $todayArchiveTimeToLive < 3600
@@ -81,8 +81,8 @@ class Piwik_CoreAdminHome_Controller extends Piwik_Controller_Admin
$enableBrowserTriggerArchiving = Piwik_Common::getRequestVar('enableBrowserTriggerArchiving');
$todayArchiveTimeToLive = Piwik_Common::getRequestVar('todayArchiveTimeToLive');
- Piwik_ArchiveProcessing::setBrowserTriggerArchiving((bool)$enableBrowserTriggerArchiving);
- Piwik_ArchiveProcessing::setTodayArchiveTimeToLive($todayArchiveTimeToLive);
+ Piwik_ArchiveProcessor_Rules::setBrowserTriggerArchiving((bool)$enableBrowserTriggerArchiving);
+ Piwik_ArchiveProcessor_Rules::setTodayArchiveTimeToLive($todayArchiveTimeToLive);
// Update email settings
$mail = array();
diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php
index e6228d52fa..b23df6d5ac 100644
--- a/plugins/CoreAdminHome/CoreAdminHome.php
+++ b/plugins/CoreAdminHome/CoreAdminHome.php
@@ -111,17 +111,17 @@ class Piwik_CoreAdminHome extends Piwik_Plugin
function purgeOutdatedArchives()
{
- $archiveTables = Piwik::getTablesArchivesInstalled();
+ $archiveTables = Piwik_DataAccess_ArchiveTableCreator::getTablesArchivesInstalled();
foreach ($archiveTables as $table) {
- if (strpos($table, 'numeric') !== false) {
- Piwik_ArchiveProcessing_Period::doPurgeOutdatedArchives($table);
- }
+ $date = Piwik_DataAccess_ArchiveTableCreator::getDateFromTableName($table);
+ list($month, $year) = explode('_', $date);
+ Piwik_DataAccess_ArchiveSelector::purgeOutdatedArchives(Piwik_Date::factory("$year-$month-15"));
}
}
function optimizeArchiveTable()
{
- $archiveTables = Piwik::getTablesArchivesInstalled();
+ $archiveTables = Piwik_DataAccess_ArchiveTableCreator::getTablesArchivesInstalled();
Piwik_OptimizeTables($archiveTables);
}
}
diff --git a/plugins/CoreAdminHome/javascripts/generalSettings.js b/plugins/CoreAdminHome/javascripts/generalSettings.js
index 6a493626c7..303e0d4e32 100644
--- a/plugins/CoreAdminHome/javascripts/generalSettings.js
+++ b/plugins/CoreAdminHome/javascripts/generalSettings.js
@@ -123,16 +123,17 @@ $(document).ready(function () {
$('#customLogo').change(function () {$("#logoUploadForm").submit()});
// trusted hosts event handling
- $('#trustedHostSettings .adminTable').on('click', '.remove-trusted-host', function (e) {
+ var trustedHostSettings = $('#trustedHostSettings');
+ trustedHostSettings.find('.adminTable').on('click', '.remove-trusted-host', function (e) {
e.preventDefault();
$(this).parent().parent().remove();
return false;
});
- $('#trustedHostSettings .add-trusted-host').click(function (e) {
+ trustedHostSettings.find('.add-trusted-host').click(function (e) {
e.preventDefault();
// append new row to the table
- $('#trustedHostSettings tbody').append('<tr>'
+ $('#trustedHostSettings').find('tbody').append('<tr>'
+ '<td><input name="trusted_host" type="text" value=""/></td>'
+ '<td><a href="#" class="remove-trusted-host">x</a></td>'
+ '</tr>');
diff --git a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js
index d1ecd3705f..32e0aa52ed 100644
--- a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js
+++ b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js
@@ -65,7 +65,7 @@
siteUrls = {},
siteCurrencies = {},
allGoals = {},
- noneText = $('#image-tracker-goal>option').text();
+ noneText = $('#image-tracker-goal').find('>option').text();
// queries Piwik for needed site info for one site
var getSiteData = function (idSite, sectionSelect, callback) {
@@ -104,11 +104,6 @@
}
);
ajaxRequest.setCallback(function (data) {
- for (var i = 0; i != data.length; ++i) {
- data[i] = JSON.parse(data[i]);
- }
-
- // set data
var currency = data[0][0].currency || '';
siteCurrencies[idSite] = currencySymbols[currency.toUpperCase()];
siteUrls[idSite] = data[1] || [];
@@ -142,7 +137,7 @@
// function that generates JS code
var generateJsCode = function () {
// get data
- var idSite = $('#js-tracker-website .custom_select_main_link').attr('siteid'),
+ var idSite = $('#js-tracker-website').find('.custom_select_main_link').attr('siteid'),
groupPageTitlesByDomain = $('#javascript-tracking-group-by-domain').is(':checked'),
mergeSubdomains = $('#javascript-tracking-all-subdomains').is(':checked'),
mergeAliasUrls = $('#javascript-tracking-all-aliases').is(':checked'),
@@ -214,13 +209,13 @@
</script>\n\
<!-- End Piwik Code -->';
- $('#javascript-text textarea').val(result)
+ $('#javascript-text').find('textarea').val(result)
};
// function that generates image tracker link
var generateImageTrackerLink = function () {
// get data ( (("https:" == document.location.protocol)?"https://' + piwikHost + '":"http://' + piwikHost + '") )
- var idSite = $('#image-tracker-website .custom_select_main_link').attr('siteid'),
+ var idSite = $('#image-tracker-website').find('.custom_select_main_link').attr('siteid'),
path = document.location.pathname,
piwikURL = ("https:" == document.location.protocol ? "https://" + piwikHost : "http://" + piwikHost) + path.substring(0, path.lastIndexOf('/')) + '/piwik.php',
actionName = $('#image-tracker-action-name').val(),
@@ -230,7 +225,7 @@
if ($('#image-tracking-goal-check').is(':checked')) {
idGoal = $('#image-tracker-goal').val();
if (idGoal) {
- revenue = $('#image-tracker-advanced-options .revenue').val();
+ revenue = $('#image-tracker-advanced-options').find('.revenue').val();
}
}
@@ -256,7 +251,7 @@
<!-- End Piwik -->';
result = result.replace("&", "&amp;", "g");
- $('#image-tracking-link textarea').val(result);
+ $('#image-tracking-link').find('textarea').val(result);
};
// on image link tracker site change, change available goals
@@ -328,11 +323,11 @@
});
// initial generation
- getSiteData($(
- '#js-tracker-website .custom_select_main_link').attr('siteid'),
+ getSiteData(
+ $('#js-tracker-website').find('.custom_select_main_link').attr('siteid'),
'#js-code-options,#image-tracking-code-options',
function () {
- var imageTrackerSiteId = $('#image-tracker-website .custom_select_main_link').attr('siteid');
+ var imageTrackerSiteId = $('#image-tracker-website').find('.custom_select_main_link').attr('siteid');
resetGoalSelectItems(imageTrackerSiteId, 'image-tracker-goal');
generateJsCode();
diff --git a/plugins/CoreAdminHome/templates/jsTrackingGenerator.twig b/plugins/CoreAdminHome/templates/jsTrackingGenerator.twig
index e50bc5c30b..86f07a84d0 100644
--- a/plugins/CoreAdminHome/templates/jsTrackingGenerator.twig
+++ b/plugins/CoreAdminHome/templates/jsTrackingGenerator.twig
@@ -2,7 +2,7 @@
{% block head %}
{{ parent() }}
- <link rel="stylesheet" href="plugins/CoreAdminHome/stylesheets/jsTrackingGenerator.css"></link>
+ <link rel="stylesheet" href="plugins/CoreAdminHome/stylesheets/jsTrackingGenerator.css" />
<script type="text/javascript" src="plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js"></script>
{% endblock %}
diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php
index 9e0ace143d..006cb98f79 100644
--- a/plugins/CoreHome/Controller.php
+++ b/plugins/CoreHome/Controller.php
@@ -219,4 +219,25 @@ class Piwik_CoreHome_Controller extends Piwik_Controller
$view->promoVideoUrl = 'http://www.youtube.com/watch?v=OslfF_EH81g';
echo $view->render();
}
+
+ /**
+ * Redirects the user to a paypal so they can donate to Piwik.
+ */
+ public function redirectToPaypal()
+ {
+ $parameters = Piwik_API_Request::getRequestArrayFromString($request = null);
+ foreach ($paramaters as $name => $param) {
+ if ($name == 'idSite'
+ || $name == 'module'
+ || $name == 'action'
+ ) {
+ unset($parameters[$name]);
+ }
+ }
+
+ $url = "https://www.paypal.com/cgi-bin/webscr?".Piwik_Url::getQueryStringFromParameters($parameters);
+
+ header("Location: $url");
+ exit;
+ }
}
diff --git a/plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php b/plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php
index 4c4a964c2e..1e761a0aae 100644
--- a/plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php
+++ b/plugins/CoreHome/DataTableRowAction/MultiRowEvolution.php
@@ -74,12 +74,13 @@ class Piwik_CoreHome_DataTableRowAction_MultiRowEvolution
/**
* Generic method to get an evolution graph or a sparkline for the row evolution popover.
* Do as much as possible from outside the controller.
+ *
* @return Piwik_ViewDataTable
*/
public function getRowEvolutionGraph()
{
$view = parent::getRowEvolutionGraph();
- $view->setCustomParameter(self::IS_MULTI_EVOLUTION_PARAM, true);
+ $view->setCustomParameter(self::IS_MULTI_EVOLUTION_PARAM, 1); // set in JS
return $view;
}
}
diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
index 8561d8734a..ab42ea3452 100644
--- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php
+++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
@@ -74,8 +74,9 @@ class Piwik_CoreHome_DataTableRowAction_RowEvolution
$this->apiMethod = Piwik_Common::getRequestVar('apiMethod', '', 'string');
if (empty($this->apiMethod)) throw new Exception("Parameter apiMethod not set.");
- $this->label = Piwik_Common::getRequestVar('label', '', 'string');
- $this->label = Piwik_Common::unsanitizeInputValue($this->label);
+ $this->label = Piwik_API_ResponseBuilder::getLabelFromRequest($_GET);
+ $this->label = $this->label[0];
+
if ($this->label === '') throw new Exception("Parameter label not set.");
$this->period = Piwik_Common::getRequestVar('period', '', 'string');
@@ -90,7 +91,7 @@ class Piwik_CoreHome_DataTableRowAction_RowEvolution
list($this->date, $lastN) =
Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution::getDateRangeAndLastN($this->period, $end);
}
- $this->segment = Piwik_Common::getRequestVar('segment', '', 'string');
+ $this->segment = Piwik_ViewDataTable::getRawSegmentFromRequest();
$this->loadEvolutionReport();
}
@@ -132,7 +133,7 @@ class Piwik_CoreHome_DataTableRowAction_RowEvolution
$parameters = array(
'method' => 'API.getRowEvolution',
- 'label' => urlencode($this->label),
+ 'label' => $this->label,
'apiModule' => $apiModule,
'apiAction' => $apiAction,
'idSite' => $this->idSite,
diff --git a/plugins/CoreHome/javascripts/autocomplete.js b/plugins/CoreHome/javascripts/autocomplete.js
index 0e5fa3d1fa..1a9a523c21 100644
--- a/plugins/CoreHome/javascripts/autocomplete.js
+++ b/plugins/CoreHome/javascripts/autocomplete.js
@@ -14,7 +14,7 @@ function switchSite(id, name, showAjaxLoading, idCanBeAll) {
$('.sites_autocomplete input').val(id);
$('.custom_select_main_link').text(name);
$('.custom_select_main_link').addClass('custom_select_loading');
- broadcast.propagateNewPage('idSite=' + id, showAjaxLoading);
+ broadcast.propagateNewPage('segment=&idSite=' + id, showAjaxLoading);
}
return false;
}
@@ -29,6 +29,15 @@ $(function () {
// sets up every un-inited site selector widget
piwik.initSiteSelectors = function () {
+ function getUrlForWebsiteId(idSite) {
+ var idSiteParam = 'idSite=' + idSite;
+ var newParameters = 'segment=&' + idSiteParam;
+ var hash = broadcast.isHashExists() ? broadcast.getHashFromUrl() : "",
+ linkUrl = piwikHelper.getCurrentQueryStringWithParametersModified(newParameters)
+ + '#' + piwikHelper.getQueryStringWithParametersModified(hash.substring(1), newParameters);
+ return linkUrl;
+ }
+
$('.sites_autocomplete').each(function () {
var selector = $(this);
@@ -117,11 +126,8 @@ $(function () {
}
}).data("ui-autocomplete")._renderItem = function (ul, item) {
$(ul).addClass('siteSelect');
-
- var idSiteParam = 'idSite=' + item.id,
- hash = broadcast.isHashExists() ? broadcast.getHashFromUrl().replace(/idSite=[0-9]+/, idSiteParam) : "",
- linkUrl = piwikHelper.getCurrentQueryStringWithParametersModified(idSiteParam) + hash,
- link = $("<a></a>").html(item.label).attr('href', linkUrl),
+ var linkUrl = getUrlForWebsiteId(item.id);
+ var link = $("<a></a>").html(item.label).attr('href', linkUrl),
listItem = $('<li></li>');
listItem.data("item.ui-autocomplete", item)
@@ -162,12 +168,10 @@ $(function () {
$('.custom_select_block', selector).on('mouseenter', function () {
$('.custom_select_ul_list li a', selector).each(function () {
- var hash = broadcast.getHashFromUrl();
- hash = hash ? hash.replace(/idSite=[0-9]+/, 'idSite=' + $(this).attr('siteid')) : "";
+ var idSite = $(this).attr('siteid');
- var queryString = piwikHelper.getCurrentQueryStringWithParametersModified(
- 'idSite=' + $(this).attr('siteid'));
- $(this).attr('href', queryString + hash);
+ var linkUrl = getUrlForWebsiteId(idSite);
+ $(this).attr('href', linkUrl);
});
});
diff --git a/plugins/CoreHome/javascripts/broadcast.js b/plugins/CoreHome/javascripts/broadcast.js
index ccfb145033..34352de732 100644
--- a/plugins/CoreHome/javascripts/broadcast.js
+++ b/plugins/CoreHome/javascripts/broadcast.js
@@ -307,10 +307,13 @@ var broadcast = {
if (paramValue == '') {
newParamValue = '';
}
+ var getQuotedRegex = function(str) {
+ return (str+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
+ };
+
if (valFromUrl != '') {
// replacing current param=value to newParamValue;
- valFromUrl = valFromUrl.replace(/\$/g, '\\$');
- valFromUrl = valFromUrl.replace(/\./g, '\\.');
+ valFromUrl = getQuotedRegex(valFromUrl);
var regToBeReplace = new RegExp(paramName + '=' + valFromUrl, 'ig');
if (newParamValue == '') {
// if new value is empty remove leading &, aswell
@@ -459,7 +462,10 @@ var broadcast = {
hashStr = url.substring(url.indexOf("#"), url.length);
}
else {
- hashStr = decodeURIComponent(location.hash);
+ locationSplit = location.href.split('#');
+ if(typeof locationSplit[1] != 'undefined') {
+ hashStr = '#' + locationSplit[1];
+ }
}
return hashStr;
@@ -485,27 +491,36 @@ var broadcast = {
},
/**
- * Returns all key-value pairs in query string of url.
- *
- * @param {string} url url to check. if undefined, null or empty, current url is used.
- * @return {object} key value pair describing query string parameters
+ * Extracts from a query strings, the request array
+ * @param queryString
+ * @returns {object}
*/
- getValuesFromUrl: function (url) {
- var searchString = this._removeHashFromUrl(url).split('?')[1] || '',
- pairs = searchString.split('&');
-
+ extractKeyValuePairsFromQueryString: function (queryString) {
+ var pairs = queryString.split('&');
var result = {};
for (var i = 0; i != pairs.length; ++i) {
// attn: split with regex has bugs in several browsers such as IE 8
// so we need to split, use the first part as key and rejoin the rest
var pair = pairs[i].split('=');
- var key = pair.shift();
+ var key = pair.shift();
result[key] = pair.join('=');
}
return result;
},
/**
+ * Returns all key-value pairs in query string of url.
+ *
+ * @param {string} url url to check. if undefined, null or empty, current url is used.
+ * @return {object} key value pair describing query string parameters
+ */
+ getValuesFromUrl: function (url) {
+ var searchString = this._removeHashFromUrl(url).split('?')[1] || '';
+ return this.extractKeyValuePairsFromQueryString(searchString);
+ },
+
+
+ /**
* help to get param value for any given url string with provided param name
* if no url is provided, it will get param from current address.
* return:
@@ -558,9 +573,12 @@ var broadcast = {
endStr = url.length;
}
var value = url.substring(startStr + param.length + 1, endStr);
- // sanitize values
- value = value.replace(/[^_%\+\-\<\>!@\$\.=,;0-9a-zA-Z]/gi, '');
+ // we sanitize values to add a protection layer against XSS
+ // &segment= value is not sanitized, since segments are designed to accept any user input
+ if(param != 'segment') {
+ value = value.replace(/[^_%~\*\+\-\<\>!@\$\.()=,;0-9a-zA-Z]/gi, '');
+ }
return value;
} else {
return '';
diff --git a/plugins/CoreHome/javascripts/calendar.js b/plugins/CoreHome/javascripts/calendar.js
index e531e375a9..9952ac281b 100644
--- a/plugins/CoreHome/javascripts/calendar.js
+++ b/plugins/CoreHome/javascripts/calendar.js
@@ -289,8 +289,8 @@
};
var togglePeriodPickers = function (showSingle) {
- $('#periodString .period-date').toggle(showSingle);
- $('#periodString .period-range').toggle(!showSingle);
+ $('#periodString').find('.period-date').toggle(showSingle);
+ $('#periodString').find('.period-range').toggle(!showSingle);
$('#calendarRangeApply').toggle(!showSingle);
};
@@ -379,13 +379,13 @@
return false;
};
- $("#otherPeriods label").on('click', function (e) {
+ $("#otherPeriods").find("label").on('click', function (e) {
var id = $(e.target).attr('for');
changePeriodOnClick($('#' + id));
});
// when non-range period is clicked, change the period & refresh the date picker
- $("#otherPeriods input").on('click', function (e) {
+ $("#otherPeriods").find("input").on('click', function (e) {
var request_URL = $(e.target).val(),
period = broadcast.getValueFromUrl('period', request_URL),
lastPeriod = selectedPeriod;
@@ -443,7 +443,7 @@
// reset date/period when opening calendar
var firstClick = true;
- $('#periodString #date').click(function () {
+ $('#periodString').find('#date').click(function () {
if (!firstClick) {
datepickerElem.datepicker('setDate', currentDate);
$('#period_id_' + piwik.period).click();
@@ -497,7 +497,7 @@
if (!isValidDate(oDateFrom)
|| !isValidDate(oDateTo)
|| oDateFrom > oDateTo) {
- $('#alert h2').text(_pk_translate('General_InvalidDateRange_js'));
+ $('#alert').find('h2').text(_pk_translate('General_InvalidDateRange_js'));
piwikHelper.modalConfirm('#alert', {});
return false;
}
diff --git a/plugins/CoreHome/javascripts/corehome.js b/plugins/CoreHome/javascripts/corehome.js
index 7c2ede9c2f..b80b54e57a 100755
--- a/plugins/CoreHome/javascripts/corehome.js
+++ b/plugins/CoreHome/javascripts/corehome.js
@@ -25,8 +25,7 @@
ajaxRequest.setLoadingElement('#header_message .loadingPiwik');
ajaxRequest.addParams({
module: 'CoreHome',
- action: 'checkForUpdates',
- token_auth: piwik.token_auth
+ action: 'checkForUpdates'
}, 'get');
ajaxRequest.setCallback(function (response) {
headerMessage.fadeOut('slow', function () {
diff --git a/plugins/CoreHome/javascripts/datatable.js b/plugins/CoreHome/javascripts/datatable.js
index 71c4da7cb8..0e4f5ffc43 100644
--- a/plugins/CoreHome/javascripts/datatable.js
+++ b/plugins/CoreHome/javascripts/datatable.js
@@ -84,7 +84,8 @@ dataTable.prototype =
'disable_generic_filters',
'columns',
'flat',
- 'include_aggregate_rows'
+ 'include_aggregate_rows',
+ 'totalRows'
];
for (var key in filters) {
@@ -1366,8 +1367,8 @@ dataTable.prototype =
// if this url is also the url of a menu item, better to click that menu item instead of
// doing AJAX request
var menuItem = null;
- $("#root>ul.nav a").each(function () {
- if ($(this).attr('name') == url) {
+ $("#root").find(">ul.nav a").each(function () {
+ if ($(this).attr('href') == url) {
menuItem = this;
return false
}
diff --git a/plugins/CoreHome/javascripts/datatable_rowactions.js b/plugins/CoreHome/javascripts/datatable_rowactions.js
index 62931dd61e..5aea12e4b1 100644
--- a/plugins/CoreHome/javascripts/datatable_rowactions.js
+++ b/plugins/CoreHome/javascripts/datatable_rowactions.js
@@ -213,6 +213,7 @@ DataTable_RowAction.prototype.getLabelFromTr = function (tr) {
if (!value) {
value = label.text();
}
+ value = value.trim();
return encodeURIComponent(value);
};
diff --git a/plugins/CoreHome/javascripts/date.js b/plugins/CoreHome/javascripts/date.js
index 1c6c5971bd..776ac417ae 100644
--- a/plugins/CoreHome/javascripts/date.js
+++ b/plugins/CoreHome/javascripts/date.js
@@ -23,18 +23,12 @@ $(document).ready(function () {
}
};
- $("#periodString #date")
- .hover(function () {
- $(this).css({ cursor: "pointer"});
- }, function () {
-
- })
- .click(function () {
- periodWidget.toggle();
- if ($("#periodMore").is(":visible")) {
- $("#periodMore .ui-state-highlight").removeClass('ui-state-highlight');
- }
- });
+ $("#periodString").on('click', "#date,.calendar-icon", function () {
+ periodWidget.toggle();
+ if ($("#periodMore").is(":visible")) {
+ $("#periodMore").find(".ui-state-highlight").removeClass('ui-state-highlight');
+ }
+ });
//close periodString onClickOutside
$('body').on('mouseup', function (e) {
diff --git a/plugins/CoreHome/javascripts/menu.js b/plugins/CoreHome/javascripts/menu.js
index 3be160b1ca..b2010152be 100644
--- a/plugins/CoreHome/javascripts/menu.js
+++ b/plugins/CoreHome/javascripts/menu.js
@@ -29,7 +29,7 @@ menu.prototype =
onItemClick: function (item) {
$('ul.nav').trigger('piwikSwitchPage', item);
- broadcast.propagateAjax($(item).attr('name'));
+ broadcast.propagateAjax( $(item).attr('href').substr(1) );
return false;
},
@@ -45,7 +45,7 @@ menu.prototype =
// for all sub menu we want to have a unique id based on their module and action
// for main menu we want to add just the module as its id.
this.menuNode.find('li').each(function () {
- var url = $(this).find('a').attr('name');
+ var url = $(this).find('a').attr('href').substr(1);
var module = broadcast.getValueFromUrl("module", url);
var action = broadcast.getValueFromUrl("action", url);
var moduleId = broadcast.getValueFromUrl("idGoal", url) || broadcast.getValueFromUrl("idDashboard", url);
diff --git a/plugins/CoreHome/templates/donate.twig b/plugins/CoreHome/templates/donate.twig
index c33dfffe9e..ad5ad8dee7 100755
--- a/plugins/CoreHome/templates/donate.twig
+++ b/plugins/CoreHome/templates/donate.twig
@@ -13,7 +13,7 @@
<div class="donate-form-instructions">({{ 'CoreHome_DonateFormInstructions'|translate }})</div>
- <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
+ <form action="index.php?module=CoreHome&action=redirectToPaypal&idSite=1" method="post" target="_blank">
<input type="hidden" name="cmd" value="_s-xclick"/>
<input type="hidden" name="hosted_button_id" value="DVKLY73RS7JTE"/>
<input type="hidden" name="currency_code" value="USD"/>
@@ -36,7 +36,7 @@
<input type="image" src="plugins/Zeitgeist/images/paypal_subscribe.gif" border="0" name="submit"
title="{{ 'CoreHome_SubscribeAndBecomePiwikSupporter'|translate }}"/>
<a class="donate-spacer">{{ 'CoreHome_MakeOneTimeDonation'|translate }}</a>
- <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RPL23NJURMTFA&bb2_screener_=1357583494+83.233.186.82"
+ <a href="index.php?module=CoreHome&action=redirectToPaypal&idSite=1&cmd=_s-xclick&hosted_button_id=RPL23NJURMTFA&bb2_screener_=1357583494+83.233.186.82"
target="_blank" class="donate-one-time">{{ 'CoreHome_MakeOneTimeDonation'|translate }}</a>
</div>
diff --git a/plugins/CoreHome/templates/html_report_header.twig b/plugins/CoreHome/templates/html_report_header.twig
index 2e74bcc038..0e6879a1c6 100644
--- a/plugins/CoreHome/templates/html_report_header.twig
+++ b/plugins/CoreHome/templates/html_report_header.twig
@@ -15,6 +15,12 @@
{{ description }} - {{ 'General_DateRange'|translate }} {{ prettyDate }}
</p>
+{% if displaySegment %}
+<p style="color: rgb({{ reportTitleTextColor }});">
+ {{ 'PDFReports_CustomVisitorSegment'|translate("Piwik") }} {{ segmentName }}
+</p>
+{% endif %}
+
{% if reportMetadata|length > 1 %}
<h2 style="color: rgb({{ reportTitleTextColor }}); font-size: {{ reportTitleTextSize }}pt;">
{{ 'PDFReports_TableOfContent'|translate }}
diff --git a/plugins/CoreHome/templates/menu.twig b/plugins/CoreHome/templates/menu.twig
index 9b7ae232c2..2d8cefed98 100644
--- a/plugins/CoreHome/templates/menu.twig
+++ b/plugins/CoreHome/templates/menu.twig
@@ -6,8 +6,10 @@
<ul>
{% for name,urlParameters in level2 %}
{% if name|slice(0,1) != '_' %}
- <li><a name='{{ urlParameters._url|urlRewriteWithParameters }}' href='#{{ urlParameters._url|urlRewriteWithParameters|slice(1) }}'
- onclick='return piwikMenu.onItemClick(this);'>{{ name|translate }}</a></li>
+ <li>
+ <a href='#{{ urlParameters._url|urlRewriteWithParameters|slice(1) }}'
+ onclick='return piwikMenu.onItemClick(this);'>{{ name|translate }}</a>
+ </li>
{% endif %}
{% endfor %}
</ul>
diff --git a/plugins/CoreHome/templates/period_select.twig b/plugins/CoreHome/templates/period_select.twig
index bf63a6d752..6b33a9fea3 100644
--- a/plugins/CoreHome/templates/period_select.twig
+++ b/plugins/CoreHome/templates/period_select.twig
@@ -2,6 +2,7 @@
<div id="periodString">
<div id="date">{{ 'General_DateRange'|translate }} <b>{{ prettyDate }}</b></div>
+ <div class="calendar-icon"></div>
<div id="periodMore">
<div class="period-date">
<h6>{{ 'General_Date'|translate }}</h6>
diff --git a/plugins/CoreHome/templates/reports_by_dimension.twig b/plugins/CoreHome/templates/reports_by_dimension.twig
index de23fe1053..acea8389b3 100644
--- a/plugins/CoreHome/templates/reports_by_dimension.twig
+++ b/plugins/CoreHome/templates/reports_by_dimension.twig
@@ -16,7 +16,7 @@
{% endfor %}
</div>
- <div style="float:left;">
+ <div style="float:left;max-width:900px;">
<div class="loadingPiwik" style="display:none">
<img src="plugins/Zeitgeist/images/loading-blue.gif" alt=""/>{{ 'General_LoadingData'|translate }}
</div>
diff --git a/plugins/CoreUpdater/Controller.php b/plugins/CoreUpdater/Controller.php
index ef57443698..387aba3d4b 100644
--- a/plugins/CoreUpdater/Controller.php
+++ b/plugins/CoreUpdater/Controller.php
@@ -163,7 +163,7 @@ class Piwik_CoreUpdater_Controller extends Piwik_Controller
/*
* Make sure the execute bit is set for this shell script
*/
- if (!Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled()) {
+ if (!Piwik_ArchiveProcessor_Rules::isBrowserTriggerEnabled()) {
@chmod($this->pathRootExtractedPiwik . '/misc/cron/archive.sh', 0755);
}
diff --git a/plugins/CustomVariables/API.php b/plugins/CustomVariables/API.php
index 3a47c837c5..338eb6d74a 100644
--- a/plugins/CustomVariables/API.php
+++ b/plugins/CustomVariables/API.php
@@ -41,9 +41,10 @@ class Piwik_CustomVariables_API
*/
protected function getDataTable($idSite, $period, $date, $segment, $expanded, $idSubtable)
{
- $dataTable = Piwik_Archive::getDataTableFromArchive('CustomVariables_valueByName', $idSite, $period, $date, $segment, $expanded, $idSubtable);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
+ $dataTable = Piwik_Archive::getDataTableFromArchive(Piwik_CustomVariables_Archiver::CUSTOM_VARIABLE_RECORD_NAME, $idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $dataTable->filter('Sort', array(Piwik_Metrics::INDEX_NB_ACTIONS, 'desc', $naturalSort = false, $expanded));
$dataTable->queueFilter('ReplaceColumnNames');
+ $dataTable->queueFilter('ColumnDelete', 'nb_uniq_visitors');
return $dataTable;
}
@@ -64,7 +65,7 @@ class Piwik_CustomVariables_API
if ($dataTable instanceof Piwik_DataTable
&& !$_leavePiwikCoreVariables
) {
- $mapping = array('_pks', '_pkn', '_pkc', '_pkp', Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT, Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY);
+ $mapping = self::getReservedCustomVariableKeys();
foreach ($mapping as $name) {
$row = $dataTable->getRowFromLabel($name);
if ($row) {
@@ -76,6 +77,15 @@ class Piwik_CustomVariables_API
}
/**
+ * @ignore
+ * @return array
+ */
+ public static function getReservedCustomVariableKeys()
+ {
+ return array('_pks', '_pkn', '_pkc', '_pkp', Piwik_Tracker_Action::CVAR_KEY_SEARCH_COUNT, Piwik_Tracker_Action::CVAR_KEY_SEARCH_CATEGORY);
+ }
+
+ /**
* @param int $idSite
* @param string $period
* @param Piwik_Date $date
@@ -96,7 +106,7 @@ class Piwik_CustomVariables_API
$dataTable->renameColumn('price_viewed', 'price');
}
$dataTable->queueFilter('ColumnCallbackReplace', array('label', create_function('$label', '
- return $label == Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED
+ return $label == Piwik_CustomVariables_Archiver::LABEL_CUSTOM_VALUE_NOT_DEFINED
? "' . Piwik_Translate('General_NotDefined', Piwik_Translate('CustomVariables_ColumnCustomVariableValue')) . '"
: $label;')));
return $dataTable;
diff --git a/plugins/CustomVariables/Archiver.php b/plugins/CustomVariables/Archiver.php
new file mode 100644
index 0000000000..4b63845215
--- /dev/null
+++ b/plugins/CustomVariables/Archiver.php
@@ -0,0 +1,198 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_CustomVariables
+ */
+
+class Piwik_CustomVariables_Archiver extends Piwik_PluginsArchiver
+{
+ const LABEL_CUSTOM_VALUE_NOT_DEFINED = "Value not defined";
+ const CUSTOM_VARIABLE_RECORD_NAME = 'CustomVariables_valueByName';
+
+ /**
+ * @var Piwik_DataArray
+ */
+ protected $dataArray;
+ protected $maximumRowsInDataTableLevelZero;
+ protected $maximumRowsInSubDataTable;
+ protected $newEmptyRow;
+
+ function __construct($processor)
+ {
+ parent::__construct($processor);
+ $this->maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_custom_variables'];
+ $this->maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_custom_variables'];
+ }
+
+ public function archiveDay()
+ {
+ $this->dataArray = new Piwik_DataArray();
+
+ for ($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++) {
+ $this->aggregateCustomVariable($i);
+ }
+
+ $this->removeVisitsMetricsFromActionsAggregate();
+ $this->dataArray->enrichMetricsWithConversions();
+ $table = $this->getProcessor()->getDataTableFromDataArray($this->dataArray);
+ $blob = $table->getSerialized(
+ $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable,
+ $columnToSort = Piwik_Metrics::INDEX_NB_VISITS
+ );
+
+ $this->getProcessor()->insertBlobRecord(self::CUSTOM_VARIABLE_RECORD_NAME, $blob);
+ }
+
+ protected function aggregateCustomVariable($slot)
+ {
+ $keyField = "custom_var_k" . $slot;
+ $valueField = "custom_var_v" . $slot;
+ $where = "%s.$keyField != ''";
+ $dimensions = array($keyField, $valueField);
+
+ $query = $this->getLogAggregator()->queryVisitsByDimension($dimensions, $where);
+ $this->aggregateFromVisits($query, $keyField, $valueField);
+
+ // IF we query Custom Variables scope "page" either: Product SKU, Product Name,
+ // then we also query the "Product page view" price which was possibly recorded.
+ $additionalSelects = false;
+ // FIXMEA
+ if (in_array($slot, array(3,4,5))) {
+ $additionalSelects = array( $this->getSelectAveragePrice() );
+ }
+ $query = $this->getLogAggregator()->queryActionsByDimension($dimensions, $where, $additionalSelects);
+ $this->aggregateFromActions($query, $keyField, $valueField);
+
+ $query = $this->getLogAggregator()->queryConversionsByDimension($dimensions, $where);
+ $this->aggregateFromConversions($query, $keyField, $valueField);
+ }
+
+ protected function getSelectAveragePrice()
+ {
+ return Piwik_DataAccess_LogAggregator::getSqlRevenue("AVG(log_link_visit_action.custom_var_v2)")
+ . " as `" . Piwik_Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED . "`";
+ }
+
+ protected function aggregateFromVisits($query, $keyField, $valueField)
+ {
+ while ($row = $query->fetch()) {
+ $key = $row[$keyField];
+ $value = $this->cleanCustomVarValue($row[$valueField]);
+
+ $this->dataArray->sumMetricsVisits($key, $row);
+ $this->dataArray->sumMetricsVisitsPivot($key, $value, $row);
+ }
+ }
+
+ protected function cleanCustomVarValue($value)
+ {
+ if (strlen($value)) {
+ return $value;
+ }
+ return self::LABEL_CUSTOM_VALUE_NOT_DEFINED;
+ }
+
+
+ protected function aggregateFromActions($query, $keyField, $valueField)
+ {
+ while ($row = $query->fetch()) {
+ $key = $row[$keyField];
+ $value = $this->cleanCustomVarValue($row[$valueField]);
+
+ $alreadyAggregated = $this->aggregateEcommerceCategories($key, $value, $row);
+ if (!$alreadyAggregated) {
+ $this->aggregateActionByKeyAndValue($key, $value, $row);
+ $this->dataArray->sumMetricsActions($key, $row);
+ }
+ }
+ }
+
+ /**
+ * @return bool True if the $row metrics were already added to the ->metrics
+ */
+ protected function aggregateEcommerceCategories($key, $value, $row)
+ {
+ $ecommerceCategoriesAggregated = false;
+ if ($key == '_pkc'
+ && $value[0] == '[' && $value[1] == '"'
+ ) {
+ // In case categories were truncated, try closing the array
+ if (substr($value, -2) != '"]') {
+ $value .= '"]';
+ }
+ $decoded = @Piwik_Common::json_decode($value);
+ if (is_array($decoded)) {
+ $count = 0;
+ foreach ($decoded as $category) {
+ if (empty($category)
+ || $count >= Piwik_Tracker_GoalManager::MAXIMUM_PRODUCT_CATEGORIES
+ ) {
+ continue;
+ }
+ $this->aggregateActionByKeyAndValue($key, $category, $row);
+ $ecommerceCategoriesAggregated = true;
+ $count++;
+ }
+ }
+ }
+ return $ecommerceCategoriesAggregated;
+ }
+
+ protected function aggregateActionByKeyAndValue($key, $value, $row)
+ {
+ $this->dataArray->sumMetricsActionsPivot($key, $value, $row);
+
+ if ($this->isReservedKey($key)) {
+ // Price tracking on Ecommerce product/category pages:
+ // the average is returned from the SQL query so the price is not "summed" like other metrics
+ $index = Piwik_Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED;
+ if (!empty($row[$index])) {
+ $this->dataArray->setRowColumnPivot($key, $value, $index, (float)$row[$index]);
+ }
+ }
+ }
+
+ protected static function isReservedKey($key)
+ {
+ return in_array($key, Piwik_CustomVariables_API::getReservedCustomVariableKeys());
+ }
+
+
+ protected function aggregateFromConversions($query, $keyField, $valueField)
+ {
+ if ($query === false) {
+ return;
+ }
+ while ($row = $query->fetch()) {
+ $key = $row[$keyField];
+ $value = $this->cleanCustomVarValue($row[$valueField]);
+ $this->dataArray->sumMetricsGoals($key, $row);
+ $this->dataArray->sumMetricsGoalsPivot($key, $value, $row);
+ }
+ }
+
+ protected function removeVisitsMetricsFromActionsAggregate()
+ {
+ $dataArray = &$this->dataArray->getDataArray();
+ foreach ($dataArray as $key => &$row) {
+ if (!self::isReservedKey($key)
+ && Piwik_DataArray::isRowActions($row)
+ ) {
+ unset($row[Piwik_Metrics::INDEX_NB_UNIQ_VISITORS]);
+ unset($row[Piwik_Metrics::INDEX_NB_VISITS]);
+ }
+ }
+ }
+
+ public function archivePeriod()
+ {
+ $nameToCount = $this->getProcessor()->aggregateDataTableReports(
+ self::CUSTOM_VARIABLE_RECORD_NAME, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable,
+ $columnToSort = Piwik_Metrics::INDEX_NB_VISITS);
+ }
+} \ No newline at end of file
diff --git a/plugins/CustomVariables/Controller.php b/plugins/CustomVariables/Controller.php
index ea00259d3e..867988a79b 100644
--- a/plugins/CustomVariables/Controller.php
+++ b/plugins/CustomVariables/Controller.php
@@ -10,49 +10,52 @@
*/
/**
- *
* @package Piwik_CustomVariables
*/
class Piwik_CustomVariables_Controller extends Piwik_Controller
{
-
- function index($fetch = false)
+ public function index($fetch = false)
{
return Piwik_View::singleReport(
Piwik_Translate('CustomVariables_CustomVariables'),
$this->getCustomVariables(true), $fetch);
}
- function getCustomVariables($fetch = false)
+ public function getCustomVariables($fetch = false)
{
$view = Piwik_ViewDataTable::factory();
$view->init($this->pluginName, __FUNCTION__, "CustomVariables.getCustomVariables", "getCustomVariablesValuesFromNameId");
$this->setPeriodVariablesView($view);
- $view->enableShowGoals();
+ $this->setMetricsVariablesView($view);
- $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_actions'));
+ $this->configureView($view);
$view->setColumnTranslation('label', Piwik_Translate('CustomVariables_ColumnCustomVariableName'));
- $view->setSortedColumn('nb_visits');
- $view->setLimit(10);
+
$view->setFooterMessage(Piwik_Translate('CustomVariables_TrackingHelp', array('<a target="_blank" href="http://piwik.org/docs/custom-variables/">', '</a>')));
- $this->setMetricsVariablesView($view);
+
return $this->renderView($view, $fetch);
}
- function getCustomVariablesValuesFromNameId($fetch = false)
+ public function getCustomVariablesValuesFromNameId($fetch = false)
{
$view = Piwik_ViewDataTable::factory();
$view->init($this->pluginName, __FUNCTION__, 'CustomVariables.getCustomVariablesValuesFromNameId');
+ $this->configureView($view);
$view->disableSearchBox();
- $view->enableShowGoals();
$view->disableExcludeLowPopulation();
- $view->setColumnsToDisplay(array('label', 'nb_visits', 'nb_actions'));
$view->setColumnTranslation('label', Piwik_Translate('CustomVariables_ColumnCustomVariableValue'));
-
return $this->renderView($view, $fetch);
}
+ protected function configureView($view)
+ {
+ $view->setColumnsToDisplay(array('label', 'nb_actions', 'nb_visits'));
+ $view->setSortedColumn('nb_actions');
+ $view->enableShowGoals();
+ }
+
+
}
diff --git a/plugins/CustomVariables/CustomVariables.php b/plugins/CustomVariables/CustomVariables.php
index d76adab5ea..3f3421fe41 100644
--- a/plugins/CustomVariables/CustomVariables.php
+++ b/plugins/CustomVariables/CustomVariables.php
@@ -14,11 +14,6 @@
*/
class Piwik_CustomVariables extends Piwik_Plugin
{
- public $archiveProcessing;
- protected $columnToSortByBeforeTruncation;
- protected $maximumRowsInDataTableLevelZero;
- protected $maximumRowsInSubDataTable;
-
public function getInformation()
{
$info = array(
@@ -146,183 +141,31 @@ class Piwik_CustomVariables extends Piwik_Plugin
));
}
- function __construct()
- {
- $this->maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_referers'];
- $this->maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_referers'];
- }
-
- protected $interestByCustomVariables = array();
- protected $interestByCustomVariablesAndValue = array();
-
/**
* Hooks on daily archive to trigger various log processing
*
* @param Piwik_Event_Notification $notification notification object
- * @return void
*/
public function archiveDay($notification)
{
- $this->interestByCustomVariables = $this->interestByCustomVariablesAndValue = array();
-
- /**
- * @var Piwik_ArchiveProcessing_Day
- */
- $this->archiveProcessing = $notification->getNotificationObject();
-
- if (!$this->archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveDayAggregate($this->archiveProcessing);
- $this->archiveDayRecordInDatabase($this->archiveProcessing);
- destroy($this->interestByCustomVariables);
- destroy($this->interestByCustomVariablesAndValue);
- }
-
- const LABEL_CUSTOM_VALUE_NOT_DEFINED = "Value not defined";
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- * @return void
- */
- protected function archiveDayAggregate(Piwik_ArchiveProcessing_Day $archiveProcessing)
- {
- for ($i = 1; $i <= Piwik_Tracker::MAX_CUSTOM_VARIABLES; $i++) {
- $keyField = "custom_var_k" . $i;
- $valueField = "custom_var_v" . $i;
- $dimensions = array($keyField, $valueField);
- $where = "%s.$keyField != ''";
-
- // Custom Vars names and values metrics for visits
- $query = $archiveProcessing->queryVisitsByDimension($dimensions, $where);
-
- while ($row = $query->fetch()) {
- // Handle case custom var value is empty
- $row[$valueField] = $this->cleanCustomVarValue($row[$valueField]);
-
- // Aggregate
- if (!isset($this->interestByCustomVariables[$row[$keyField]])) $this->interestByCustomVariables[$row[$keyField]] = $archiveProcessing->getNewInterestRow();
- if (!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]] = $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariables[$row[$keyField]]);
- $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]]);
- }
-
- // Custom Vars names and values metrics for page views
- $query = $archiveProcessing->queryActionsByDimension($dimensions, $where);
- $onlyMetricsAvailableInActionsTable = true;
- while ($row = $query->fetch()) {
- // Handle case custom var value is empty
- $row[$valueField] = $this->cleanCustomVarValue($row[$valueField]);
-
- $label = $row[$valueField];
-
- // Remove price tracked if it's zero or we if we are not currently tracking an ecommerce var
- if (!in_array($row[$keyField], array('_pks', '_pkn', '_pkc'))) {
- unset($row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED]);
- }
-
- // when custom variable value is a JSON array of categories
- // possibly JSON value
- $mustInsertCustomVariableValue = true;
- if ($row[$keyField] == '_pkc'
- && $label[0] == '[' && $label[1] == '"'
- ) {
- // In case categories were truncated, try closing the array
- if (substr($label, -2) != '"]') {
- $label .= '"]';
- }
- $decoded = @Piwik_Common::json_decode($label);
- if (is_array($decoded)) {
- $count = 0;
- foreach ($decoded as $category) {
- if (empty($category)
- || $count >= Piwik_Tracker_GoalManager::MAXIMUM_PRODUCT_CATEGORIES
- ) {
- continue;
- }
- if (!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$category])) {
- $this->interestByCustomVariablesAndValue[$row[$keyField]][$category] = $archiveProcessing->getNewInterestRow($onlyMetricsAvailableInActionsTable);
- }
- $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$category], $onlyMetricsAvailableInActionsTable);
- $mustInsertCustomVariableValue = false;
- $count++;
- }
- }
- } // end multi categories hack
+ $archiveProcessor = $notification->getNotificationObject();
- if ($mustInsertCustomVariableValue) {
- if (!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]] = $archiveProcessing->getNewInterestRow($onlyMetricsAvailableInActionsTable);
- $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]], $onlyMetricsAvailableInActionsTable);
- }
-
- // Do not report on Price viewed for the Custom Variable names
- unset($row[Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED]);
-
- // When tracking Custom Variables with scope=page we do not add up visits numbers
- // as it is incorrect to sum visits this way
- // for scope=visit this is allowed, since there is supposed to be one custom var value per custom variable name for a given visit
- $doNotSumVisits = true;
-
- if (!isset($this->interestByCustomVariables[$row[$keyField]])) $this->interestByCustomVariables[$row[$keyField]] = $archiveProcessing->getNewInterestRow($onlyMetricsAvailableInActionsTable, $doNotSumVisits);
- $archiveProcessing->updateInterestStats($row, $this->interestByCustomVariables[$row[$keyField]], $onlyMetricsAvailableInActionsTable, $doNotSumVisits);
- }
-
- // Custom Vars names and values metrics for Goals
- $query = $archiveProcessing->queryConversionsByDimension($dimensions, $where);
-
- if ($query !== false) {
- while ($row = $query->fetch()) {
- // Handle case custom var value is empty
- $row[$valueField] = $this->cleanCustomVarValue($row[$valueField]);
-
- if (!isset($this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- if (!isset($this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
-
- $archiveProcessing->updateGoalStats($row, $this->interestByCustomVariables[$row[$keyField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- $archiveProcessing->updateGoalStats($row, $this->interestByCustomVariablesAndValue[$row[$keyField]][$row[$valueField]][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- }
- }
- }
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByCustomVariables);
- $archiveProcessing->enrichConversionsByLabelArrayHasTwoLevels($this->interestByCustomVariablesAndValue);
- }
-
- protected function cleanCustomVarValue($value)
- {
- if (strlen($value)) {
- return $value;
+ $archiving = new Piwik_CustomVariables_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archiveDay();
}
- return self::LABEL_CUSTOM_VALUE_NOT_DEFINED;
- }
-
- /**
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @return void
- */
- protected function archiveDayRecordInDatabase($archiveProcessing)
- {
- $recordName = 'CustomVariables_valueByName';
- $table = $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByCustomVariablesAndValue, $this->interestByCustomVariables);
-
- $blob = $table->getSerialized(
- $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable,
- $columnToSort = Piwik_Archive::INDEX_NB_VISITS);
- $archiveProcessing->insertBlobRecord($recordName, $blob);
- destroy($table);
}
/**
* @param Piwik_Event_Notification $notification notification object
- * @return mixed
*/
function archivePeriod($notification)
{
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = 'CustomVariables_valueByName';
- $nameToCount = $archiveProcessing->archiveDataTable(
- $dataTableToSum, null, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable,
- $columnToSort = Piwik_Archive::INDEX_NB_VISITS);
+ $archiveProcessor = $notification->getNotificationObject();
+ $archiving = new Piwik_CustomVariables_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
+ }
}
+
}
diff --git a/plugins/DBStats/API.php b/plugins/DBStats/API.php
index fec0e0fe71..693c7f2e1e 100644
--- a/plugins/DBStats/API.php
+++ b/plugins/DBStats/API.php
@@ -128,9 +128,7 @@ class Piwik_DBStats_API
$rowToAddTo['row_count'] += $status['Rows'];
}
- $result = new Piwik_DataTable();
- $result->addRowsFromArrayWithIndexLabel($rows);
- return $result;
+ return Piwik_DataTable::makeFromIndexedArray($rows);
}
/**
diff --git a/plugins/DBStats/MySQLMetadataProvider.php b/plugins/DBStats/MySQLMetadataProvider.php
index 6240a24b19..be487346cb 100755
--- a/plugins/DBStats/MySQLMetadataProvider.php
+++ b/plugins/DBStats/MySQLMetadataProvider.php
@@ -243,7 +243,6 @@ class Piwik_DBStats_MySQLMetadataProvider
array($cols, 'estimated_size', $getEstimatedSize, array($status)));
$dataTable->addDataTable($table);
- destroy($table);
}
return $dataTable;
}
diff --git a/plugins/Dashboard/API.php b/plugins/Dashboard/API.php
index 14a058b2aa..59ae8b9ae1 100644
--- a/plugins/Dashboard/API.php
+++ b/plugins/Dashboard/API.php
@@ -62,7 +62,7 @@ class Piwik_Dashboard_API
*
* @return array[]
*/
- public function getDefaultDashboard()
+ private function getDefaultDashboard()
{
$defaultLayout = $this->dashboard->getDefaultLayout();
$defaultLayout = $this->dashboard->decodeLayout($defaultLayout);
@@ -79,7 +79,7 @@ class Piwik_Dashboard_API
*
* @return array[]
*/
- public function getUserDashboards()
+ private function getUserDashboards()
{
$userLogin = Piwik::getCurrentUserLogin();
$userDashboards = $this->dashboard->getAllDashboards($userLogin);
diff --git a/plugins/Dashboard/Controller.php b/plugins/Dashboard/Controller.php
index 204cf8714d..ec63d82e4a 100644
--- a/plugins/Dashboard/Controller.php
+++ b/plugins/Dashboard/Controller.php
@@ -153,8 +153,11 @@ class Piwik_Dashboard_Controller extends Piwik_Controller
public function getAllDashboards()
{
$this->checkTokenInUrl();
+
if (Piwik::isUserIsAnonymous()) {
+ Piwik_DataTable_Renderer_Json::sendHeaderJSON();
echo '[]';
+
return;
}
diff --git a/plugins/Dashboard/Dashboard.php b/plugins/Dashboard/Dashboard.php
index 47a8dfedea..716e055bd7 100644
--- a/plugins/Dashboard/Dashboard.php
+++ b/plugins/Dashboard/Dashboard.php
@@ -209,7 +209,6 @@ class Piwik_Dashboard extends Piwik_Plugin
$pos++;
}
}
-
}
}
diff --git a/plugins/Dashboard/javascripts/dashboard.js b/plugins/Dashboard/javascripts/dashboard.js
index 298298e44c..e23ff669a4 100644
--- a/plugins/Dashboard/javascripts/dashboard.js
+++ b/plugins/Dashboard/javascripts/dashboard.js
@@ -24,7 +24,7 @@ function initDashboard(dashboardId, dashboardLayout) {
if (!$('#topBars').length) {
$('#dashboardSettings').css({left: 0});
$('#dashboardSettings').after($('#Dashboard'));
- $('#Dashboard > ul li a').each(function () {$(this).css({width: this.offestWidth + 30, paddingLeft: 0, paddingRight: 0});});
+ $('#Dashboard').find('> ul li a').each(function () {$(this).css({width: this.offestWidth + 30, paddingLeft: 0, paddingRight: 0});});
$('#Dashboard_embeddedIndex_' + dashboardId).addClass('sfHover');
}
@@ -36,7 +36,7 @@ function initDashboard(dashboardId, dashboardLayout) {
$('#removeDashboardLink').show();
}
// fix position
- $('#dashboardSettings .widgetpreview-widgetlist').css('paddingTop', $('#dashboardSettings .widgetpreview-categorylist').parent('li').position().top);
+ $('#dashboardSettings').find('.widgetpreview-widgetlist').css('paddingTop', $('#dashboardSettings').find('.widgetpreview-categorylist').parent('li').position().top);
});
$('body').on('mouseup', function (e) {
if (!$(e.target).parents('#dashboardSettings').length && !$(e.target).is('#dashboardSettings')) {
@@ -56,7 +56,7 @@ function initDashboard(dashboardId, dashboardLayout) {
$('#dashboardSettings').widgetPreview({
isWidgetAvailable: function (widgetUniqueId) {
- return !$('#dashboardWidgetsArea [widgetId=' + widgetUniqueId + ']').length;
+ return !$('#dashboardWidgetsArea').find('[widgetId=' + widgetUniqueId + ']').length;
},
onSelect: function (widgetUniqueId) {
var widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
@@ -66,7 +66,7 @@ function initDashboard(dashboardId, dashboardLayout) {
resetOnSelect: true
});
- $('#columnPreview>div').each(function () {
+ $('#columnPreview').find('>div').each(function () {
var width = [];
$('div', this).each(function () {
width.push(this.className.replace(/width-/, ''));
@@ -74,8 +74,8 @@ function initDashboard(dashboardId, dashboardLayout) {
$(this).attr('layout', width.join('-'));
});
- $('#columnPreview>div').on('click', function () {
- $('#columnPreview>div').removeClass('choosen');
+ $('#columnPreview').find('>div').on('click', function () {
+ $('#columnPreview').find('>div').removeClass('choosen');
$(this).addClass('choosen');
});
@@ -122,15 +122,15 @@ function renameDashboard() {
}
function removeDashboard() {
- $('#removeDashboardConfirm h2 span').html($('#dashboardWidgetsArea').dashboard('getDashboardName'));
+ $('#removeDashboardConfirm').find('h2 span').text($('#dashboardWidgetsArea').dashboard('getDashboardName'));
piwikHelper.modalConfirm('#removeDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('removeDashboard'); }});
}
function showChangeDashboardLayoutDialog() {
- $('#columnPreview>div').removeClass('choosen');
- $('#columnPreview>div[layout=' + $('#dashboardWidgetsArea').dashboard('getColumnLayout') + ']').addClass('choosen');
+ $('#columnPreview').find('>div').removeClass('choosen');
+ $('#columnPreview').find('>div[layout=' + $('#dashboardWidgetsArea').dashboard('getColumnLayout') + ']').addClass('choosen');
piwikHelper.modalConfirm('#changeDashboardLayout', {yes: function () {
- $('#dashboardWidgetsArea').dashboard('setColumnLayout', $('#changeDashboardLayout .choosen').attr('layout'));
+ $('#dashboardWidgetsArea').dashboard('setColumnLayout', $('#changeDashboardLayout').find('.choosen').attr('layout'));
}});
}
@@ -159,12 +159,12 @@ function copyDashboardToUser() {
function (availableUsers) {
$('#copyDashboardUser').empty();
$('#copyDashboardUser').append(
- $('<option></option>').val(piwik.userLogin).html(piwik.userLogin)
+ $('<option></option>').val(piwik.userLogin).text(piwik.userLogin)
);
$.each(availableUsers, function (index, user) {
if (user.login != 'anonymous' && user.login != piwik.userLogin) {
$('#copyDashboardUser').append(
- $('<option></option>').val(user.login).html(user.login + ' (' + user.alias + ')')
+ $('<option></option>').val(user.login).text(user.login + ' (' + user.alias + ')')
);
}
});
@@ -189,7 +189,7 @@ function copyDashboardToUser() {
}, 'post');
ajaxRequest.setCallback(
function (id) {
- $('#alert h2').text(_pk_translate('Dashboard_DashboardCopied_js'));
+ $('#alert').find('h2').text(_pk_translate('Dashboard_DashboardCopied_js'));
piwikHelper.modalConfirm('#alert', {});
}
);
diff --git a/plugins/Dashboard/javascripts/dashboardObject.js b/plugins/Dashboard/javascripts/dashboardObject.js
index 8863317c9c..b8f3dbb19d 100644
--- a/plugins/Dashboard/javascripts/dashboardObject.js
+++ b/plugins/Dashboard/javascripts/dashboardObject.js
@@ -434,7 +434,7 @@
function buildMenu() {
var success = function (dashboards) {
- var dashboardMenuList = $('#Dashboard > ul');
+ var dashboardMenuList = $('#Dashboard').find('> ul');
dashboardMenuList.empty();
if (dashboards.length > 1) {
dashboardMenuList.show();
diff --git a/plugins/DevicesDetection/API.php b/plugins/DevicesDetection/API.php
new file mode 100644
index 0000000000..e3e7100328
--- /dev/null
+++ b/plugins/DevicesDetection/API.php
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_DevicesDetection
+ */
+class Piwik_DevicesDetection_API
+{
+
+ static private $instance = null;
+
+ /**
+ *
+ * @return Piwik_DevicesDetection_API
+ */
+ static public function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * @param string $name
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param string $segment
+ * @return DataTable
+ */
+ protected function getDataTable($name, $idSite, $period, $date, $segment)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
+ $dataTable = $archive->getDataTable($name);
+ $dataTable->filter('Sort', array(Piwik_Metrics::INDEX_NB_VISITS));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ $dataTable->queueFilter('ReplaceSummaryRowLabel');
+ return $dataTable;
+ }
+
+ /**
+ * Gets datatable displaying number of visits by device type (eg. desktop, smartphone, tablet)
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param string $segment
+ * @return DataTable
+ */
+ public function getType($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('DevicesDetection_types', $idSite, $period, $date, $segment);
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getDeviceTypeLabel'));
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'ucfirst'));
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getDeviceTypeLogo'));
+ return $dataTable;
+ }
+
+ /**
+ * Gets datatable displaying number of visits by device manufacturer name
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param string $segment
+ * @return DataTable
+ */
+ public function getBrand($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('DevicesDetection_brands', $idSite, $period, $date, $segment);
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getDeviceBrandLabel'));
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_GetBrandLogo'));
+ return $dataTable;
+ }
+
+ /**
+ * Gets datatable displaying number of visits by device model
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param string $segment
+ * @return DataTable
+ */
+ public function getModel($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('DevicesDetection_models', $idSite, $period, $date, $segment);
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getModelName'));
+ return $dataTable;
+ }
+
+ /**
+ * Gets datatable displaying number of visits by OS family (eg. Windows, Android, Linux)
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param string $segment
+ * @return DataTable
+ */
+ public function getOsFamilies($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('DevicesDetection_os', $idSite, $period, $date, $segment);
+ $dataTable->filter('GroupBy', array('label', 'Piwik_getOSFamilyFullNameExtended'));
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getOsFamilyLogoExtended'));
+ return $dataTable;
+ }
+
+ /**
+ * Gets datatable displaying number of visits by OS version (eg. Android 4.0, Windows 7)
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param string $segment
+ * @return DataTable
+ */
+ public function getOsVersions($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('DevicesDetection_osVersions', $idSite, $period, $date, $segment);
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getOsLogoExtended'));
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getOsFullNameExtended'));
+
+ return $dataTable;
+ }
+
+ /**
+ * Gets datatable displaying number of visits by Browser family (eg. Firefox, InternetExplorer)
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param string $segment
+ * @return DataTable
+ */
+ public function getBrowserFamilies($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('DevicesDetection_browsers', $idSite, $period, $date, $segment);
+ $dataTable->filter('GroupBy', array('label', 'Piwik_getBrowserFamilyFullNameExtended'));
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowserFamilyLogoExtended'));
+ return $dataTable;
+ }
+
+ /**
+ * Gets datatable displaying number of visits by Browser version (eg. Firefox 20, Safari 6.0)
+ * @param int $idSite
+ * @param string $period
+ * @param string $date
+ * @param string $segment
+ * @return DataTable
+ */
+ public function getBrowserVersions($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getDataTable('DevicesDetection_browserVersions', $idSite, $period, $date, $segment);
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowserLogoExtended'));
+ $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserNameExtended'));
+ return $dataTable;
+ }
+
+} \ No newline at end of file
diff --git a/plugins/DevicesDetection/Archiver.php b/plugins/DevicesDetection/Archiver.php
new file mode 100644
index 0000000000..5b8114e412
--- /dev/null
+++ b/plugins/DevicesDetection/Archiver.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_DevicesDetection
+ */
+
+class Piwik_DevicesDetection_Archiver extends Piwik_PluginsArchiver
+{
+ const DEVICE_TYPE_RECORD_NAME = 'DevicesDetection_types';
+ const DEVICE_BRAND_RECORD_NAME = 'DevicesDetection_brands';
+ const DEVICE_MODEL_RECORD_NAME = 'DevicesDetection_models';
+ const OS_RECORD_NAME = 'DevicesDetection_os';
+ const OS_VERSION_RECORD_NAME = 'DevicesDetection_osVersions';
+ const BROWSER_RECORD_NAME = 'DevicesDetection_browsers';
+ const BROWSER_VERSION_RECORD_NAME = 'DevicesDetection_browserVersions';
+
+ const DEVICE_TYPE_FIELD = "config_device_type";
+ const DEVICE_BRAND_FIELD = "config_device_brand";
+ const DEVICE_MODEL_FIELD = "config_device_model";
+ const OS_FIELD = "config_os";
+ const OS_VERSION_FIELD = "CONCAT(log_visit.config_os, ';', log_visit.config_os_version)";
+ const BROWSER_FIELD = "config_browser_name";
+ const BROWSER_VERSION_DIMENSION = "CONCAT(log_visit.config_browser_name, ';', log_visit.config_browser_version)";
+
+ public function archiveDay()
+ {
+ $this->aggregateByLabel( self::DEVICE_TYPE_FIELD, self::DEVICE_TYPE_RECORD_NAME);
+ $this->aggregateByLabel( self::DEVICE_BRAND_FIELD, self::DEVICE_BRAND_RECORD_NAME);
+ $this->aggregateByLabel( self::DEVICE_MODEL_FIELD, self::DEVICE_MODEL_RECORD_NAME);
+ $this->aggregateByLabel( self::OS_FIELD, self::OS_RECORD_NAME);
+ $this->aggregateByLabel( self::OS_VERSION_FIELD, self::OS_VERSION_RECORD_NAME);
+ $this->aggregateByLabel( self::BROWSER_FIELD, self::BROWSER_RECORD_NAME);
+ $this->aggregateByLabel( self::BROWSER_VERSION_DIMENSION, self::BROWSER_VERSION_RECORD_NAME);
+ }
+
+ private function aggregateByLabel( $labelSQL, $recordName)
+ {
+ $metrics = $this->getProcessor()->getMetricsForDimension($labelSQL);
+ $table = $this->getProcessor()->getDataTableFromDataArray($metrics);
+ $this->getProcessor()->insertBlobRecord($recordName, $table->getSerialized($this->maximumRows, null, Piwik_Metrics::INDEX_NB_VISITS));
+ }
+
+ public function archivePeriod()
+ {
+ $dataTablesToSum = array(
+ self::DEVICE_TYPE_RECORD_NAME,
+ self::DEVICE_BRAND_RECORD_NAME,
+ self::DEVICE_MODEL_RECORD_NAME,
+ self::OS_RECORD_NAME,
+ self::OS_VERSION_RECORD_NAME,
+ self::BROWSER_RECORD_NAME,
+ self::BROWSER_VERSION_RECORD_NAME
+ );
+ foreach ($dataTablesToSum as $dt) {
+ $this->getProcessor()->aggregateDataTableReports(
+ $dt, $this->maximumRows, $this->maximumRows, $columnToSort = "nb_visits");
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/DevicesDetection/Controller.php b/plugins/DevicesDetection/Controller.php
new file mode 100644
index 0000000000..22681f1a6c
--- /dev/null
+++ b/plugins/DevicesDetection/Controller.php
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_DevicesDetection
+ */
+class Piwik_DevicesDetection_Controller extends Piwik_Controller
+{
+
+ /** The set of related reports displayed under the 'Operating Systems' header. */
+ private $osRelatedReports = null;
+ private $browserRelatedReports = null;
+
+ public function __construct()
+ {
+ parent::__construct();
+ $this->osRelatedReports = array(
+ 'DevicesDetection.getOsFamilies' => Piwik_Translate('DeviceDetection_OperatingSystemFamilies'),
+ 'DevicesDetection.getOsVersions' => Piwik_Translate('DeviceDetection_OperatingSystemVersions')
+ );
+ $this->browserRelatedReports = array(
+ 'DevicesDetection.getBrowserFamilies' => Piwik_Translate('DevicesDetection_BrowsersFamily'),
+ 'DevicesDetection.getBrowserVersions' => Piwik_Translate('DevicesDetection_BrowserVersions')
+ );
+ }
+
+ public function index($fetch = false)
+ {
+ $view = Piwik_View::factory('index');
+ $view->deviceTypes = $view->deviceModels = $view->deviceBrands = $view->osReport = $view->browserReport = "blank";
+ $view->deviceTypes = $this->getType(true);
+ $view->deviceBrands = $this->getBrand(true);
+ $view->deviceModels = $this->getModel(true);
+ $view->osReport = $this->getOsFamilies(true);
+ $view->browserReport = $this->getBrowserFamilies(true);
+ echo $view->render();
+ }
+
+ public function getType($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__, 'DevicesDetection.getType'
+ );
+
+ $view->setColumnTranslation('label', Piwik_Translate("DevicesDetection_dataTableLabelTypes"));
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getBrand($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__, 'DevicesDetection.getBrand'
+ );
+
+ $view->setColumnTranslation('label', Piwik_Translate("DevicesDetection_dataTableLabelBrands"));
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getModel($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__, 'DevicesDetection.getModel'
+ );
+
+ $view->setColumnTranslation('label', Piwik_Translate("DevicesDetection_dataTableLabelModels"));
+
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getOsFamilies($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__, 'DevicesDetection.getOsFamilies'
+ );
+
+ $view->setColumnTranslation('label', Piwik_Translate("DevicesDetection_dataTableLabelSystemFamily"));
+ $view->addRelatedReports(Piwik_Translate('DeviceDetection_OperatingSystemFamilies'), $this->osRelatedReports);
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getOsVersions($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__, 'DevicesDetection.getOsVersions'
+ );
+
+ $view->setColumnTranslation('label', Piwik_Translate("DevicesDetection_dataTableLabelSystemVersion"));
+ $view->addRelatedReports(Piwik_Translate('DeviceDetection_OperatingSystemVersions'), $this->osRelatedReports);
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getBrowserFamilies($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__, 'DevicesDetection.getBrowserFamilies'
+ );
+
+ $view->setColumnTranslation('label', Piwik_Translate("DevicesDetection_dataTableLabelBrowserFamily"));
+ $view->addRelatedReports(Piwik_Translate('DevicesDetection_BrowsersFamily'), $this->browserRelatedReports);
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getBrowserVersions($fetch = false)
+ {
+ $view = $this->getStandardDataTableUserSettings(
+ __FUNCTION__, 'DevicesDetection.getBrowserVersions'
+ );
+
+ $view->setColumnTranslation('label', Piwik_Translate("DevicesDetection_dataTableLabelBrowserVersion"));
+ $view->addRelatedReports(Piwik_Translate('DevicesDetection_BrowserVersions'), $this->browserRelatedReports);
+ return $this->renderView($view, $fetch);
+ }
+
+ protected function getStandardDataTableUserSettings($currentControllerAction, $APItoCall, $defaultDatatableType = null)
+ {
+ $view = Piwik_ViewDataTable::factory($defaultDatatableType);
+ $view->init($this->pluginName, $currentControllerAction, $APItoCall);
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $this->setPeriodVariablesView($view);
+ $this->setMetricsVariablesView($view);
+ return $view;
+ }
+
+ /**
+ * You may manually call this controller action to force re-processing of past user agents
+ */
+ public function refreshParsedUserAgents()
+ {
+ $q = "SELECT idvisit, config_debug_ua FROM " . Piwik_Common::prefixTable("log_visit");
+ $res = Piwik_FetchAll($q);
+ foreach ($res as $rec) {
+ $UAParser = new UserAgentParserEnhanced($rec['config_debug_ua']);
+ $UAParser->parse();
+ echo "Processing idvisit = " . $rec['idvisit'] . "<br/>";
+ echo "UserAgent string: " . $rec['config_debug_ua'] . "<br/> Decoded values:";
+ $uaDetails = $this->getArray($UAParser);
+ var_export($uaDetails);
+ echo "<hr/>";
+ $this->updateVisit($rec['idvisit'], $uaDetails);
+ unset($UAParser);
+ }
+ echo "Please remember to truncate your archives !";
+ }
+
+ private function getArray(UserAgentParserEnhanced $UAParser)
+ {
+ $UADetails['config_browser_name'] = $UAParser->getBrowser("short_name");
+ $UADetails['config_browser_version'] = $UAParser->getBrowser("version");
+ $UADetails['config_os'] = $UAParser->getOs("short_name");
+ $UADetails['config_os_version'] = $UAParser->getOs("version");
+ $UADetails['config_device_type'] = $UAParser->getDevice();
+ $UADetails['config_device_model'] = $UAParser->getModel();
+ $UADetails['config_device_brand'] = $UAParser->getBrand();
+ return $UADetails;
+ }
+
+ private function updateVisit($idVisit, $uaDetails)
+ {
+ $q = "UPDATE " . Piwik_Common::prefixTable("log_visit") . " SET " .
+ "config_browser_name = '" . $uaDetails['config_browser_name'] . "' ," .
+ "config_browser_version = '" . $uaDetails['config_browser_version'] . "' ," .
+ "config_os = '" . $uaDetails['config_os'] . "' ," .
+ "config_os_version = '" . $uaDetails['config_os_version'] . "' ," .
+ "config_device_type = " . (isset($uaDetails['config_device_type']) ? "'" . $uaDetails['config_device_type'] . "'" : "NULL") . " ," .
+ "config_device_model = " . (isset($uaDetails['config_device_model']) ? "'" . $uaDetails['config_device_model'] . "'" : "NULL") . " ," .
+ "config_device_brand = " . (isset($uaDetails['config_device_brand']) ? "'" . $uaDetails['config_device_brand'] . "'" : "NULL") . "
+ WHERE idvisit = " . $idVisit;
+ Piwik_Query($q);
+ }
+
+} \ No newline at end of file
diff --git a/plugins/DevicesDetection/DevicesDetection.php b/plugins/DevicesDetection/DevicesDetection.php
new file mode 100644
index 0000000000..30c55ffe37
--- /dev/null
+++ b/plugins/DevicesDetection/DevicesDetection.php
@@ -0,0 +1,264 @@
+<?php
+
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_DevicesDetection
+ */
+require_once PIWIK_INCLUDE_PATH . "/plugins/DevicesDetection/UserAgentParserEnhanced/UserAgentParserEnhanced.php";
+require_once PIWIK_INCLUDE_PATH . '/plugins/DevicesDetection/functions.php';
+
+class Piwik_DevicesDetection extends Piwik_Plugin
+{
+ /**
+ * Return information about this plugin.
+ * @return array
+ */
+ public function getInformation()
+ {
+ return array(
+ 'description' => "[Beta Plugin] " . Piwik_Translate("DevicesDetection_description"),
+ 'author' => 'Piwik and Clearcode.cc',
+ 'author_homepage' => 'http://clearcode.cc',
+ 'version' => '1.12-b6',
+ 'TrackerPlugin' => true,
+ 'translationAvailable' => true,
+ );
+ }
+
+ /*
+ * Defines API reports.
+ * Also used to define Widgets, and Segment(s)
+ *
+ * @return array Category, Report Name, API Module, API action, Translated column name, & optional segment info
+ *
+ */
+ protected function getRawMetadataReports()
+ {
+ $report = array(
+ array(
+ 'DevicesDetection_DevicesDetection',
+ 'DevicesDetection_DeviceType',
+ 'DevicesDetection',
+ 'getType',
+ 'DevicesDetection_DeviceType',
+
+ // Segment
+ 'deviceType',
+ 'log_visit.config_device_type',
+ implode(", ", UserAgentParserEnhanced::$deviceTypes), // comma separated examples
+ create_function('$type', 'return array_search( strtolower(trim(urldecode($type))), UserAgentParserEnhanced::$deviceTypes);')
+ ),
+ // device brands report
+ array(
+ 'DevicesDetection_DevicesDetection',
+ 'DevicesDetection_DeviceBrand',
+ 'DevicesDetection',
+ 'getBrand',
+ 'DevicesDetection_DeviceBrand',
+ ),
+ // device model report
+ array(
+ 'DevicesDetection_DevicesDetection',
+ 'DevicesDetection_DeviceModel',
+ 'DevicesDetection',
+ 'getModel',
+ 'DevicesDetection_DeviceModel',
+ ),
+ // device OS family report
+ array(
+ 'DevicesDetection_DevicesDetection',
+ 'DeviceDetection_OperatingSystemFamilies',
+ 'DevicesDetection',
+ 'getOsFamilies',
+ 'DeviceDetection_OperatingSystemFamilies',
+ ),
+ // device OS version report
+ array(
+ 'DevicesDetection_DevicesDetection',
+ 'DeviceDetection_OperatingSystemVersions',
+ 'DevicesDetection',
+ 'getOsVersions',
+ 'DeviceDetection_OperatingSystemVersions',
+ ),
+ // Browser family report
+ array(
+ 'DevicesDetection_DevicesDetection',
+ 'DevicesDetection_BrowsersFamily',
+ 'DevicesDetection',
+ 'getBrowserFamilies',
+ 'DevicesDetection_BrowsersFamily',
+ ),
+ // Browser versions report
+ array(
+ 'DevicesDetection_DevicesDetection',
+ 'DevicesDetection_BrowserVersions',
+ 'DevicesDetection',
+ 'getBrowserVersions',
+ 'DevicesDetection_BrowserVersions',
+ ),
+ );
+ return $report;
+ }
+
+ public function getListHooksRegistered()
+ {
+ return array(
+ 'ArchiveProcessing_Day.compute' => "archiveDay",
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'Menu.add' => 'addMenu',
+ 'Tracker.newVisitorInformation' => 'parseMobileVisitData',
+ 'WidgetsList.add' => 'addWidgets',
+ 'API.getReportMetadata' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ );
+ }
+
+ public function addWidgets()
+ {
+ foreach ($this->getRawMetadataReports() as $report) {
+ list($category, $name, $controllerName, $controllerAction) = $report;
+ if ($category == false)
+ continue;
+ Piwik_AddWidget($category, $name, $controllerName, $controllerAction);
+ }
+ }
+
+
+ /**
+ * Get segments meta data
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ // Note: only one field segmented so far: deviceType
+ $segments =& $notification->getNotificationObject();
+ foreach ($this->getRawMetadataReports() as $report) {
+ @list($category, $name, $apiModule, $apiAction, $columnName, $segment, $sqlSegment, $acceptedValues) = $report;
+
+ if (empty($segment)) continue;
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => Piwik_Translate('General_Visit'),
+ 'name' => $columnName,
+ 'segment' => $segment,
+ 'acceptedValues' => $acceptedValues,
+ 'sqlSegment' => $sqlSegment,
+ 'sqlFilter' => isset($sqlFilter) ? $sqlFilter : false,
+ );
+ }
+ }
+
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $reports = & $notification->getNotificationObject();
+
+ $i = 0;
+ foreach ($this->getRawMetadataReports() as $report) {
+ list($category, $name, $apiModule, $apiAction, $columnName) = $report;
+ if ($category == false)
+ continue;
+
+ $report = array(
+ 'category' => Piwik_Translate($category),
+ 'name' => Piwik_Translate($name),
+ 'module' => $apiModule,
+ 'action' => $apiAction,
+ 'dimension' => Piwik_Translate($columnName),
+ 'order' => $i++
+ );
+
+ $translation = $name . 'Documentation';
+ $translated = Piwik_Translate($translation, '<br />');
+ if ($translated != $translation) {
+ $report['documentation'] = $translated;
+ }
+
+
+ $reports[] = $report;
+ }
+ }
+
+ public function install()
+ {
+// we catch the exception
+ try {
+ $q1 = "ALTER TABLE `" . Piwik_Common::prefixTable("log_visit") . "`
+ ADD `config_os_version` VARCHAR( 10 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL AFTER `config_os` ,
+ ADD `config_device_type` TINYINT( 10 ) NULL DEFAULT NULL AFTER `config_browser_version` ,
+ ADD `config_device_brand` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL AFTER `config_device_type` ,
+ ADD `config_device_model` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL AFTER `config_device_brand`";
+ Piwik_Exec($q1);
+ // conditionaly add this column
+ if (@Piwik_Config::getInstance()->Debug['store_user_agent_in_visit']) {
+ $q2 = "ALTER TABLE `" . Piwik_Common::prefixTable("log_visit") . "`
+ ADD `config_debug_ua` VARCHAR( 512 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL AFTER `config_device_model`";
+ Piwik_Exec($q2);
+ }
+ } catch (Exception $e) {
+ if (!Zend_Registry::get('db')->isErrNo($e, '1060')) {
+ throw $e;
+ }
+ }
+ }
+
+ public function parseMobileVisitData($notification)
+ {
+ $visitorInfo = &$notification->getNotificationObject();
+
+ $extraInfo = $notification->getNotificationInfo();
+ $userAgent = $extraInfo['UserAgent'];
+
+ $UAParser = new UserAgentParserEnhanced($userAgent);
+ $UAParser->parse();
+ $deviceInfo['config_browser_name'] = $UAParser->getBrowser("short_name");
+ $deviceInfo['config_browser_version'] = $UAParser->getBrowser("version");
+ $deviceInfo['config_os'] = $UAParser->getOs("short_name");
+ $deviceInfo['config_os_version'] = $UAParser->getOs("version");
+ $deviceInfo['config_device_type'] = $UAParser->getDevice();
+ $deviceInfo['config_device_model'] = $UAParser->getModel();
+ $deviceInfo['config_device_brand'] = $UAParser->getBrand();
+
+ if (@Piwik_Config::getInstance()->Debug['store_user_agent_in_visit']) {
+ $deviceInfo['config_debug_ua'] = $userAgent;
+ }
+
+ $visitorInfo = array_merge($visitorInfo, $deviceInfo);
+ printDebug("Device Detection:");
+ printDebug($deviceInfo);
+ }
+
+ public function archiveDay($notification)
+ {
+ $archiveProcessor = $notification->getNotificationObject();
+
+ $archiving = new Piwik_DevicesDetection_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archiveDay();
+ }
+ }
+
+ public function archivePeriod($notification)
+ {
+ $archiveProcessor = $notification->getNotificationObject();
+ $archiving = new Piwik_DevicesDetection_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
+ }
+ }
+
+ public function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', 'DevicesDetection_submenu', array('module' => 'DevicesDetection', 'action' => 'index'));
+ }
+
+} \ No newline at end of file
diff --git a/plugins/DevicesDetection/UserAgentParserEnhanced/UserAgentParserEnhanced.php b/plugins/DevicesDetection/UserAgentParserEnhanced/UserAgentParserEnhanced.php
new file mode 100644
index 0000000000..911c751221
--- /dev/null
+++ b/plugins/DevicesDetection/UserAgentParserEnhanced/UserAgentParserEnhanced.php
@@ -0,0 +1,735 @@
+<?php
+
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_DevicesDetection
+ */
+//yml parser
+require_once(PIWIK_INCLUDE_PATH.'/libs/spyc.php');
+
+class UserAgentParserEnhanced
+{
+ public static $deviceTypes = array( 'desktop', 'smartphone', 'tablet', 'feature phone', 'console', 'tv', 'car browser' );
+
+ public static $deviceBrands = array(
+ 'AC' => 'Acer',
+ 'AI' => 'Airness',
+ 'AL' => 'Alcatel',
+ 'AO' => 'Amoi',
+ 'AP' => 'Apple',
+ 'AU' => 'Asus',
+ 'AV' => 'Avvio',
+ 'AX' => 'Audiovox',
+ 'BE' => 'Becker',
+ 'BI' => 'Bird',
+ 'BL' => 'Beetel',
+ 'BQ' => 'BenQ',
+ 'BS' => 'BenQ-Siemens',
+ 'CK' => 'Cricket',
+ 'CL' => 'Compal',
+ 'CT' => 'Capitel',
+ 'DB' => 'Dbtel',
+ 'DC' => 'DoCoMo',
+ 'DI' => 'Dicam',
+ 'DL' => 'Dell',
+ 'DP' => 'Dopod',
+ 'EC' => 'Ericsson',
+ 'EI' => 'Ezio',
+ 'ER' => 'Ericy',
+ 'ET' => 'eTouch',
+ 'EZ' => 'Ezze',
+ 'FL' => 'Fly',
+ 'GI' => 'Gionee',
+ 'GO' => 'Google',
+ 'GR' => 'Gradiente',
+ 'GU' => 'Grundig',
+ 'HA' => 'Haier',
+ 'HP' => 'HP',
+ 'HT' => 'HTC',
+ 'HU' => 'Huawei',
+ 'IK' => 'iKoMo',
+ 'IM' => 'i-mate',
+ 'IN' => 'Innostream',
+ 'IO' => 'i-mobile',
+ 'IQ' => 'INQ',
+ 'KA' => 'Karbonn',
+ 'KD' => 'KDDI',
+ 'KN' => 'Kindle',
+ 'KO' => 'Konka',
+ 'KY' => 'Kyocera',
+ 'LA' => 'Lanix',
+ 'LC' => 'LCT',
+ 'LE' => 'Lenovo',
+ 'LG' => 'LG',
+ 'LU' => 'LGUPlus',
+ 'MI' => 'MicroMax',
+ 'MO' => 'Mio',
+ 'MR' => 'Motorola',
+ 'MS' => 'Microsoft',
+ 'MT' => 'Mitsubishi',
+ 'MY' => 'MyPhone',
+ 'NE' => 'NEC',
+ 'NG' => 'NGM',
+ 'NI' => 'Nintendo',
+ 'NK' => 'Nokia',
+ 'NW' => 'Newgen',
+ 'NX' => 'Nexian',
+ 'OD' => 'Onda',
+ 'OP' => 'OPPO',
+ 'OR' => 'Orange',
+ 'OT' => 'O2',
+ 'PA' => 'Panasonic',
+ 'PH' => 'Philips',
+ 'PM' => 'Palm',
+ 'PO' => 'phoneOne',
+ 'PT' => 'Pantech',
+ 'QT' => 'Qtek',
+ 'RM' => 'RIM',
+ 'RO' => 'Rover',
+ 'SA' => 'Samsung',
+ 'SD' => 'Sega',
+ 'SE' => 'Sony Ericsson',
+ 'SF' => 'Softbank',
+ 'SG' => 'Sagem',
+ 'SH' => 'Sharp',
+ 'SI' => 'Siemens',
+ 'SN' => 'Sendo',
+ 'SO' => 'Sony',
+ 'SP' => 'Spice',
+ 'SY' => 'Sanyo',
+ 'TA' => 'Tesla',
+ 'TC' => 'TCL',
+ 'TE' => 'Telit',
+ 'TH' => 'TiPhone',
+ 'TI' => 'TIANYU',
+ 'TM' => 'T-Mobile',
+ 'TO' => 'Toplux',
+ 'TS' => 'Toshiba',
+ 'UT' => 'UTStarcom',
+ 'VD' => 'Videocon',
+ 'VE' => 'Vertu',
+ 'VI' => 'Vitelcom',
+ 'VK' => 'VK Mobile',
+ 'VO' => 'Voxtel',
+ 'WB' => 'Web TV',
+ 'WE' => 'WellcoM',
+ 'WO' => 'Wonu',
+ 'XX' => 'Unknown',
+ 'ZO' => 'Zonda',
+ 'ZT' => 'ZTE',
+ );
+ public static $osShorts = array(
+ 'AIX' => 'AIX',
+ 'Android' => 'AND',
+ 'Apple TV' => 'ATV',
+ 'Arch Linux' => 'ARL',
+ 'BackTrack' => 'BTR',
+ 'Bada' => 'SBA',
+ 'BlackBerry OS' => 'BLB',
+ 'BlackBerry Tablet OS' => 'QNX',
+ 'Bot' => 'BOT',
+ 'Brew' => 'BMP',
+ 'CentOS' => 'CES',
+ 'Chrome OS' => 'COS',
+ 'Debian' => 'DEB',
+ 'DragonFly' => 'DFB',
+ 'Fedora' => 'FED',
+ 'Firefox OS' => 'FOS',
+ 'FreeBSD' => 'BSD',
+ 'Gentoo' => 'GNT',
+ 'Google TV' => 'GTV',
+ 'HP-UX' => 'HPX',
+ 'IRIX' => 'IRI',
+ 'Knoppix' => 'KNO',
+ 'Kubuntu' => 'KBT',
+ 'Linux' => 'LIN',
+ 'Lubuntu' => 'LBT',
+ 'Mac' => 'MAC',
+ 'Mandriva' => 'MDR',
+ 'MeeGo' => 'SMG',
+ 'Mint' => 'MIN',
+ 'NetBSD' => 'NBS',
+ 'Nintendo' => 'WII',
+ 'Nintendo Mobile' => 'NDS',
+ 'OS/2' => 'OS2',
+ 'OSF1' => 'T64',
+ 'OpenBSD' => 'OBS',
+ 'PlayStation' => 'PSP',
+ 'PlayStation 3' => 'PS3',
+ 'Presto' => 'PRS',
+ 'Puppy' => 'PPY',
+ 'Red Hat' => 'RHT',
+ 'SUSE' => 'SSE',
+ 'Slackware' => 'SLW',
+ 'Solaris' => 'SOS',
+ 'Syllable' => 'SYL',
+ 'Symbian' => 'SYM',
+ 'Symbian OS' => 'SYS',
+ 'Symbian OS Series 40' => 'S40',
+ 'Symbian OS Series 60' => 'S60',
+ 'Symbian^3' => 'SY3',
+ 'Talkatone' => 'TKT',
+ 'Tizen' => 'TIZ',
+ 'Ubuntu' => 'UBT',
+ 'WebTV' => 'WTV',
+ 'WinWAP' => 'WWP',
+ 'Windows' => 'WIN',
+ 'Windows 2000' => 'W2K',
+ 'Windows 3.1' => 'W31',
+ 'Windows 7' => 'WI7',
+ 'Windows 8' => 'WI8',
+ 'Windows 95' => 'W95',
+ 'Windows 98' => 'W98',
+ 'Windows CE' => 'WCE',
+ 'Windows ME' => 'WME',
+ 'Windows Mobile' => 'WMO',
+ 'Windows NT' => 'WNT',
+ 'Windows Phone' => 'WPH',
+ 'Windows RT' => 'WRT',
+ 'Windows Server 2003' => 'WS3',
+ 'Windows Vista' => 'WVI',
+ 'Windows XP' => 'WXP',
+ 'Xbox' => 'XBX',
+ 'Xubuntu' => 'XBT',
+ 'YunOs' => 'YNS',
+ 'iOS' => 'IOS',
+ 'palmOS' => 'POS',
+ 'webOS' => 'WOS'
+ );
+ protected static $desktopOsArray = array('IBM', 'Linux', 'Mac', 'Unix', 'Windows');
+ public static $osFamilies = array(
+ 'Android' => array('AND'),
+ 'Apple TV' => array('ATV'),
+ 'BlackBerry' => array('BLB'),
+ 'Bot' => array('BOT'),
+ 'Brew' => array('BMP'),
+ 'Chrome OS' => array('COS'),
+ 'Firefox OS' => array('FOS'),
+ 'Gaming Console' => array('WII', 'PS3'),
+ 'Google TV' => array('GTV'),
+ 'IBM' => array('OS2'),
+ 'iOS' => array('IOS'),
+ 'Linux' => array('LIN', 'ARL', 'DEB', 'KNO', 'MIN', 'UBT', 'KBT', 'XBT', 'LBT', 'FED', 'RHT', 'MDR', 'GNT', 'SLW', 'SSE', 'PPY', 'CES', 'BTR', 'YNS', 'PRS'),
+ 'Mac' => array('MAC'),
+ 'Mobile Gaming Console' => array('PSP', 'NDS', 'XBX'),
+ 'Other Mobile' => array('WOS', 'POS', 'QNX', 'SBA', 'TIZ'),
+ 'Simulator' => array('TKT', 'WWP'),
+ 'Symbian' => array('SYM', 'SYS', 'SY3', 'S60', 'S40', 'SMG'),
+ 'Unix' => array('SOS', 'AIX', 'HPX', 'BSD', 'NBS', 'OBS', 'DFB', 'SYL', 'IRI', 'T64'),
+ 'WebTV' => array('WTV'),
+ 'Windows' => array('WI8', 'WI7', 'WVI', 'WS3', 'WXP', 'W2K', 'WNT', 'WME', 'W98', 'W95', 'WRT', 'W31', 'WIN'),
+ 'Windows Mobile' => array('WPH', 'WMO', 'WCE')
+ );
+ public static $browserFamilies = array(
+ 'Android Browser' => array('AN'),
+ 'BlackBerry Browser' => array('BB'),
+ 'Chrome' => array('CH', 'CM', 'CI', 'CF', 'CR', 'RM'),
+ 'Firefox' => array('FF', 'FE', 'SX', 'FB', 'PX', 'MB'),
+ 'Internet Explorer' => array('IE', 'IM'),
+ 'Konqueror' => array('KO'),
+ 'NetFront' => array('NF'),
+ 'Nokia Browser' => array('NB'),
+ 'Opera' => array('OP', 'OM', 'OI'),
+ 'Safari' => array('SF', 'MF')
+ );
+ public static $browsers = array(
+ 'AB' => 'ABrowse',
+ 'AM' => 'Amaya',
+ 'AN' => 'Android Browser',
+ 'AR' => 'Arora',
+ 'AV' => 'Amiga Voyager',
+ 'AW' => 'Amiga Aweb',
+ 'BB' => 'BlackBerry Browser',
+ 'BD' => 'Baidu Browser',
+ 'BE' => 'Beonex',
+ 'BX' => 'BrowseX',
+ 'CA' => 'Camino',
+ 'CF' => 'Chrome Frame',
+ 'CH' => 'Chrome',
+ 'CI' => 'Chrome Mobile iOS',
+ 'CK' => 'Conkeror',
+ 'CM' => 'Chrome Mobile',
+ 'CO' => 'CometBird',
+ 'CR' => 'Chromium',
+ 'CS' => 'Cheshire',
+ 'DF' => 'Dolphin',
+ 'DI' => 'Dillo',
+ 'EL' => 'Elinks',
+ 'EP' => 'Epiphany',
+ 'FB' => 'Firebird',
+ 'FD' => 'Fluid',
+ 'FE' => 'Fennec',
+ 'FF' => 'Firefox',
+ 'FL' => 'Flock',
+ 'FN' => 'Fireweb Navigator',
+ 'GA' => 'Galeon',
+ 'GE' => 'Google Earth',
+ 'HJ' => 'HotJava',
+ 'IB' => 'IBrowse',
+ 'IC' => 'iCab',
+ 'IE' => 'Internet Explorer',
+ 'IM' => 'IE Mobile',
+ 'IR' => 'Iron',
+ 'JS' => 'Jasmine',
+ 'KI' => 'Kindle Browser',
+ 'KM' => 'K-meleon',
+ 'KO' => 'Konqueror',
+ 'KP' => 'Kapiko',
+ 'KZ' => 'Kazehakase',
+ 'LG' => 'Lightning',
+ 'LI' => 'Links',
+ 'LX' => 'Lynx',
+ 'MB' => 'MicroB',
+ 'MC' => 'NCSA Mosaic',
+ 'MF' => 'Mobile Safari',
+ 'MI' => 'Midori',
+ 'MS' => 'Mobile Silk',
+ 'MX' => 'Maxthon',
+ 'NB' => 'Nokia Browser',
+ 'NF' => 'NetFront',
+ 'NL' => 'NetFront Life',
+ 'NS' => 'Netscape',
+ 'OB' => 'Obigo',
+ 'OI' => 'Opera Mini',
+ 'OM' => 'Opera Mobile',
+ 'OP' => 'Opera',
+ 'OV' => 'Openwave Mobile Browser',
+ 'OW' => 'OmniWeb',
+ 'PL' => 'Palm Blazer',
+ 'PR' => 'Palm Pre',
+ 'PX' => 'Phoenix',
+ 'RK' => 'Rekonq',
+ 'RM' => 'RockMelt',
+ 'SF' => 'Safari',
+ 'SM' => 'SeaMonkey',
+ 'SN' => 'Snowshoe',
+ 'SX' => 'Swiftfox',
+ 'TZ' => 'Tizen Browser',
+ 'UC' => 'UC Browser',
+ 'WO' => 'wOSBrowser',
+ 'YA' => 'Yandex Browser'
+ );
+
+ const UNKNOWN = "UNK";
+ protected static $regexesDir = '/regexes/';
+ protected static $osRegexesFile = 'oss.yml';
+ protected static $browserRegexesFile = 'browsers.yml';
+ protected static $mobileRegexesFile = 'mobiles.yml';
+ protected $userAgent;
+ protected $os;
+ protected $browser;
+ protected $device;
+ protected $brand;
+ protected $model;
+ protected $debug = false;
+
+ public function __construct($userAgent)
+ {
+ $this->userAgent = $userAgent;
+ }
+
+ protected function getOsRegexes()
+ {
+ return Spyc::YAMLLoad(dirname(__FILE__) . self::$regexesDir . self::$osRegexesFile);
+ }
+
+ protected function getBrowserRegexes()
+ {
+ return Spyc::YAMLLoad(dirname(__FILE__) . self::$regexesDir . self::$browserRegexesFile);
+ }
+
+ protected function getMobileRegexes()
+ {
+ return Spyc::YAMLLoad(dirname(__FILE__) . self::$regexesDir . self::$mobileRegexesFile);
+ }
+
+ public function parse()
+ {
+ $this->parseOs();
+ if ($this->isBot() || $this->isSimulator())
+ return;
+
+ $this->parseBrowser();
+
+ if ($this->isMobile()) {
+ $this->parseMobile();
+ } else {
+ $this->device = array_search('desktop', self::$deviceTypes);
+ }
+ if ($this->debug) {
+ var_export($this->brand, $this->model, $this->device);
+ }
+ }
+
+ protected function parseOs()
+ {
+ foreach ($this->getOsRegexes() as $osRegex) {
+ $matches = $this->matchUserAgent($osRegex['regex']);
+ if ($matches)
+ break;
+ }
+
+ if (!$matches)
+ return;
+
+ if (in_array($osRegex['name'], self::$osShorts)) {
+ $short = self::$osShorts[$osRegex['name']];
+ } else {
+ $short = 'UNK';
+ }
+
+ $this->os = array(
+ 'name' => $this->buildOsName($osRegex['name'], $matches),
+ 'short_name' => $short,
+ 'version' => $this->buildOsVersion($osRegex['version'], $matches)
+ );
+
+ if (array_key_exists($this->os['name'], self::$osShorts)) {
+ $this->os['short_name'] = self::$osShorts[$this->os['name']];
+ }
+ }
+
+ protected function parseBrowser()
+ {
+ foreach ($this->getBrowserRegexes() as $browserRegex) {
+ $matches = $this->matchUserAgent($browserRegex['regex']);
+ if ($matches)
+ break;
+ }
+
+ if (!$matches)
+ return;
+
+ if (in_array($browserRegex['name'], self::$browsers)) {
+ $short = array_search($browserRegex['name'], self::$browsers);
+ } else {
+ $short = 'XX';
+ }
+
+ $this->browser = array(
+ 'name' => $this->buildBrowserName($browserRegex['name'], $matches),
+ 'short_name' => $short,
+ 'version' => $this->buildBrowserVersion($browserRegex['version'], $matches)
+ );
+ }
+
+ protected function parseMobile()
+ {
+ $mobileRegexes = $this->getMobileRegexes();
+ $this->parseBrand($mobileRegexes);
+ $this->parseModel($mobileRegexes);
+ }
+
+ protected function parseBrand($mobileRegexes)
+ {
+ foreach ($mobileRegexes as $brand => $mobileRegex) {
+ $matches = $this->matchUserAgent($mobileRegex['regex']);
+ if ($matches)
+ break;
+ }
+
+ if (!$matches)
+ return;
+ $this->brand = array_search($brand, self::$deviceBrands);
+ $this->fullName = $brand;
+
+ if (isset($mobileRegex['device'])) {
+ $this->device = array_search($mobileRegex['device'],self::$deviceTypes);
+ }
+
+ if (isset($mobileRegex['model'])) {
+ $this->model = $this->buildModel($mobileRegex['model'], $matches);
+ }
+ }
+
+ protected function parseModel($mobileRegexes)
+ {
+ if (empty($this->brand) || !empty($this->model))
+ return;
+
+ foreach ($mobileRegexes[$this->fullName]['models'] as $modelRegex) {
+ $matches = $this->matchUserAgent($modelRegex['regex']);
+ if ($matches)
+ break;
+ }
+
+ if (!$matches) {
+ return;
+ }
+
+ $this->model = $this->buildModel($modelRegex['model'], $matches);
+
+ if (isset($modelRegex['device'])) {
+ $this->device = array_search($modelRegex['device'], self::$deviceTypes);
+ }
+ }
+
+ protected function matchUserAgent($regex)
+ {
+ $regex = '/' . str_replace('/', '\/', $regex) . '/i';
+
+ if (preg_match($regex, $this->userAgent, $matches)) {
+ return $matches;
+ }
+
+ return false;
+ }
+
+ protected function buildOsName($osName, $matches)
+ {
+ return $this->buildByMatch($osName, $matches);
+ }
+
+ protected function buildOsVersion($osVersion, $matches)
+ {
+ $osVersion = $this->buildByMatch($osVersion, $matches);
+
+ $osVersion = $this->buildByMatch($osVersion, $matches, '2');
+
+ $osVersion = str_replace('_', '.', $osVersion);
+
+ return $osVersion;
+ }
+
+ protected function buildBrowserName($browserName, $matches)
+ {
+ return $this->buildByMatch($browserName, $matches);
+ }
+
+ protected function buildBrowserVersion($browserVersion, $matches)
+ {
+ $browserVersion = $this->buildByMatch($browserVersion, $matches);
+
+ $browserVersion = $this->buildByMatch($browserVersion, $matches, '2');
+
+ $browserVersion = str_replace('_', '.', $browserVersion);
+
+ return $browserVersion;
+ }
+
+ protected function buildModel($model, $matches)
+ {
+ $model = $this->buildByMatch($model, $matches);
+
+ $model = $this->buildByMatch($model, $matches, '2');
+
+ $model = $this->buildModelExceptions($model);
+
+ $model = str_replace('_', ' ', $model);
+
+ return $model;
+ }
+
+ protected function buildModelExceptions($model)
+ {
+ if ($this->brand == 'O2') {
+ $model = preg_replace('/([a-z])([A-Z])/', '$1 $2', $model);
+ $model = ucwords(str_replace('_', ' ', $model));
+ }
+
+ return $model;
+ }
+
+ /**
+ * This method is used in this class for processing results of pregmatch
+ * results into string containing recognized information.
+ *
+ * General algorithm:
+ * Parsing UserAgent string consists of trying to match it against list of
+ * regular expressions for three different information:
+ * browser + version,
+ * OS + version,
+ * device manufacturer + model.
+ *
+ * After match has been found iteration stops, and results are processed
+ * by buildByMatch.
+ * As $item we get decoded name (name of browser, name of OS, name of manufacturer).
+ * In array $match we recieve preg_match results containing whole string matched at index 0
+ * and following matches in further indexes. Desired action now is to concatenate
+ * decoded name ($item) with matches found. First step is to append first found match,
+ * which is located in index=1 (that's why $nb is 1 by default).
+ * In other cases, where whe know that preg_match may return more than 1 result,
+ * we call buildByMatch with $nb = 2 or more, depending on what will be returned from
+ * regular expression.
+ *
+ * Example:
+ * We are parsing UserAgent of Firefox 20.0 browser.
+ * UserAgentParserEnhanced calls buildBrowserName() and buildBrowserVersion() in order
+ * to retrieve those information.
+ * In buildBrowserName() we only have one call of buildByMatch, where passed argument
+ * is regular expression testing given string for browser name. In this case, we are only
+ * interrested in first hit, so no $nb parameter will be set to 1. After finding match, and calling
+ * buildByMatch - we will receive just the name of browser.
+ *
+ * Also after decoding browser we will get list of regular expressions for this browser name
+ * testing UserAgent string for version number. Again we iterate over this list, and after finding first
+ * occurence - we break loop and proceed to build by match. Since browser regular expressions can
+ * contain two hits (major version and minor version) in function buildBrowserVersion() we have
+ * two calls to buildByMatch, one without 3rd parameter, and second with $nb set to 2.
+ * This way we can retrieve version number, and assign it to object property.
+ *
+ * In case of mobiles.yml this schema slightly varies, but general idea is the same.
+ *
+ * @param string $item
+ * @param array $matches
+ * @param int $nb
+ * @return type
+ */
+ protected function buildByMatch($item, $matches, $nb = '1')
+ {
+ if (strpos($item, '$' . $nb) === false)
+ return $item;
+
+ $replace = isset($matches[$nb]) ? $matches[$nb] : '';
+ return trim(str_replace('$' . $nb, $replace, $item));
+ }
+
+ public function isBot()
+ {
+ $decodedFamily = '';
+ if (in_array($this->getOs('name'), self::$osShorts)) {
+ $osShort = self::$osShorts[$this->getOs('name')];
+ } else {
+ $osShort = '';
+ }
+ foreach (self::$osFamilies as $family => $familyOs) {
+ if (in_array($osShort, $familyOs)) {
+ $decodedFamily = $family;
+ break;
+ }
+ }
+
+ return $decodedFamily == 'Bot';
+ }
+
+ public function isSimulator()
+ {
+ $decodedFamily = '';
+ if (in_array($this->getOs('name'), self::$osShorts)) {
+ $osShort = self::$osShorts[$this->getOs('name')];
+ } else {
+ $osShort = '';
+ }
+ foreach (self::$osFamilies as $family => $familyOs) {
+ if (in_array($osShort, $familyOs)) {
+ $decodedFamily = $family;
+ break;
+ }
+ }
+ return $decodedFamily == 'Simulator';
+ }
+
+ public function isMobile()
+ {
+ return !$this->isDesktop();
+ }
+
+ public function isDesktop()
+ {
+ $osName = $this->getOs('name');
+ if (empty($osName) || empty(self::$osShorts[$osName])) {
+ return false;
+ }
+
+ $osShort = self::$osShorts[$osName];
+ foreach (self::$osFamilies as $family => $familyOs) {
+ if (in_array($osShort, $familyOs)) {
+ $decodedFamily = $family;
+ break;
+ }
+ }
+ return in_array($decodedFamily, self::$desktopOsArray);
+ }
+
+ public function getOs($attr = '')
+ {
+ if ($attr == '') {
+ return $this->os;
+ }
+
+ if (!isset($this->os[$attr])) {
+ return self::UNKNOWN;
+ }
+
+ if ($attr == 'version') {
+ $this->os['version'] = $this->os['version'];
+ }
+ return $this->os[$attr];
+ }
+
+ public function getBrowser($attr = '')
+ {
+ if ($attr == '') {
+ return $this->browser;
+ }
+
+ if (!isset($this->browser[$attr])) {
+ return self::UNKNOWN;
+ }
+
+ return $this->browser[$attr];
+ }
+
+ public function getDevice()
+ {
+ return $this->device;
+ }
+
+ public function getBrand()
+ {
+ return $this->brand;
+ }
+
+ public function getModel()
+ {
+ return $this->model;
+ }
+
+ public function getUserAgent()
+ {
+ return $this->userAgent;
+ }
+
+ public static function getOsFamily($osLabel)
+ {
+ $osShortName = substr($osLabel, 0, 3);
+
+ foreach (self::$osFamilies as $osFamily => $osShortNames) {
+ if (in_array($osShortName, $osShortNames)) {
+ return $osFamily;
+ }
+ }
+
+ return 'Other';
+ }
+
+ public static function getBrowserFamily($browserLabel)
+ {
+ foreach (self::$browserFamilies as $browserFamily => $browserShortNames) {
+ if (in_array($browserLabel, $browserShortNames)) {
+ return $browserFamily;
+ }
+ }
+
+ return 'Other';
+ }
+
+ public static function getOsNameFromId($os, $ver = false)
+ {
+ $osFullName = array_search($os, self::$osShorts);
+ if ($osFullName) {
+ if (in_array($os, self::$osFamilies['Windows'])) {
+ return $osFullName;
+ } else {
+ return trim($osFullName . " " . $ver);
+ }
+ }
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/browsers.yml b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/browsers.yml
new file mode 100644
index 0000000000..0a4d2a6669
--- /dev/null
+++ b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/browsers.yml
@@ -0,0 +1,408 @@
+###############
+# Piwik - Open source web analytics
+#
+# @link http://piwik.org
+# @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+#
+# @category Piwik_Plugins
+# @package Piwik_DevicesDetection
+###############
+
+# SeaMonkey
+- regex: '(Iceape|SeaMonkey)/(\d+\.\d+)'
+ name: $1
+ version: '$2'
+
+# Camino
+- regex: 'Camino/(\d+\.\d+)'
+ name: Camino
+ version: '$1'
+
+#Fennec (Firefox for mobile)
+- regex: 'Fennec/(\d+\.\d+)'
+ name: Fennec
+ version: '$1'
+
+#MicroB
+- regex: 'Firefox.*Tablet browser (\d+\.\d+)'
+ name: MicroB
+ version: '$1'
+
+#Firefox
+- regex: 'Firefox/(\d+\.\d+)'
+ name: Firefox
+ version: '$1'
+- regex: '(BonEcho|GranParadiso|Lorentz|Minefield|Namoroka|Shiretoko)/(\d+\.\d+)'
+ name: Firefox '$1'
+ version: '$2'
+
+#Flock
+- regex: 'Flock/(\d+\.\d+)'
+ name: Flock
+ version: '$1'
+
+#RockMelt
+- regex: 'RockMelt/(\d+\.\d+)'
+ name: RockMelt
+ version: '$1'
+
+#Netscape
+- regex: '(?:Navigator|Netscape6)/(\d+\.\d+)'
+ name: Netscape
+ version: '$1'
+
+#Opera
+- regex: '(:?Opera Tablet.*Version|Opera/.+Opera Mobi.+Version|Safari.*OPR)/(\d+\.\d+)'
+ name: Opera Mobile
+ version: '$2'
+- regex: 'Opera Mini/(:?att/)?(\d+\.\d+)'
+ name: Opera Mini
+ version: '$2'
+- regex: 'Opera[/ ](?:9.80.*Version/)?(\d+\.\d+)'
+ name: Opera
+ version: '$1'
+
+#wOSBrowser
+- regex: '(?:hpw|web)OS/(\d+\.\d+)'
+ name: wOSBrowser
+ version: '$1'
+
+#Swiftfox
+- regex: 'Firefox/(\d+\.\d+).*\(Swiftfox\)'
+ name: Swiftfox
+ version: '$1'
+
+#Rekonq
+- regex: 'rekonq'
+ name: Rekonq
+ version: ''
+
+#Conkeror
+- regex: 'Conkeror/(\d+\.\d+)'
+ name: Conkeror
+ version: '$1'
+
+#Konqueror
+- regex: 'Konqueror/(\d+\.\d+)'
+ name: Konqueror
+ version: '$1'
+
+#Baidu Browser
+- regex: 'baidubrowser[/ ](\d+)'
+ name: Baidu Browser
+ version: '$1'
+
+#Yandex Browser
+- regex: 'YaBrowser/(\d+)'
+ name: Yandex Browser
+ version: '$1'
+
+#Chrome
+- regex: 'CrMo/(\d+\.\d+)'
+ name: Chrome Mobile
+ version: '$1'
+- regex: 'CriOS/(\d+\.\d+)'
+ name: Chrome Mobile iOS
+ version: '$1'
+- regex: 'Chrome/(\d+\.\d+).*Mobile'
+ name: Chrome Mobile
+ version: '$1'
+- regex: 'chromeframe/(\d+\.\d+)'
+ name: Chrome Frame
+ version: '$1'
+- regex: 'Chrome/(\d+\.\d+)'
+ name: Chrome
+ version: '$1'
+- regex: 'Chromium/(\d+\.\d+)'
+ name: Chromium
+ version: '$1'
+
+#UC Browser
+- regex: 'UC[ ]?Browser[ /](\d+\.\d+)'
+ name: UC Browser
+ version: '$1'
+- regex: '(?:UC Browser|UCBrowser|UCWEB)(\d+\.\d+)'
+ name: UC Browser
+ version: '$1'
+
+#Tizen Browser
+- regex: '(?:Tizen|SLP) Browser/(\d+\.\d+)'
+ name: Tizen Browser
+ version: '$1'
+
+#Epiphany
+- regex: 'Epiphany/(\d+\.\d+)'
+ name: Epiphany
+ version: '$1'
+
+#Fireweb Navigator
+- regex: 'Fireweb Navigator/(\d+\.\d+)'
+ name: Fireweb Navigator
+ version: '$1'
+
+#Jasmine
+- regex: 'Jasmine[ /](\d+\.\d+)'
+ name: Jasmine
+ version: '$1'
+
+#Lynx
+- regex: 'Lynx/(\d+\.\d+)'
+ name: Lynx
+ version: '$1'
+
+#Midori
+- regex: 'Midori/(\d+\.\d+)'
+ name: Midori
+ version: '$1'
+
+#NCSA Mosaic
+- regex: 'NCSA_Mosaic/(\d+\.\d+)'
+ name: NCSA Mosaic
+ version: '$1'
+
+#ABrowse
+- regex: 'ABrowse (\d+\.\d+)'
+ name: ABrowse
+ version: '$1'
+
+#Amaya
+- regex: 'amaya/(\d+\.\d+)'
+ name: Amaya
+ version: '$1'
+
+#Amiga Voyager
+- regex: 'AmigaVoyager/(\d+\.\d+)'
+ name: Amiga Voyager
+ version: '$1'
+
+#Amiga Aweb
+- regex: 'Amiga-Aweb/(\d+\.\d+)'
+ name: Amiga Aweb
+ version: '$1'
+
+#Arora
+- regex: 'Arora/(\d+\.\d+)'
+ name: Arora
+ version: '$1'
+
+#Beonex
+- regex: 'Beonex/(\d+\.\d+)'
+ name: Beonex
+ version: '$1'
+
+#BlackBerry Browser
+- regex: 'Black[bB]erry|PlayBook|BB10'
+ name: BlackBerry Browser
+ version: ''
+
+#BrowseX
+- regex: 'BrowseX \((\d+\.\d+)'
+ name: BrowseX
+ version: '$1'
+
+#Cheshire
+- regex: 'Cheshire/(\d+\.\d+)'
+ name: Cheshire
+ version: '$1'
+
+#CometBird
+- regex: 'CometBird/(\d+\.\d+)'
+ name: CometBird
+ version: '$1'
+
+#Dillo
+- regex: 'Dillo/(\d+\.\d+)'
+ name: Dillo
+ version: '$1'
+
+#Dolphin
+- regex: 'Dolfin/(\d+\.\d+)|dolphin'
+ name: Dolphin
+ version: '$1'
+
+#Elinks
+- regex: 'Elinks/(\d+\.\d+)'
+ name: Elinks
+ version: '$1'
+
+#Firebird
+- regex: 'Firebird/(\d+\.\d+)'
+ name: Firebird
+ version: '$1'
+
+#Fluid
+- regex: 'Fluid/(\d+\.\d+)'
+ name: Fluid
+ version: '$1'
+
+#Galeon
+- regex: 'Galeon/(\d+\.\d+)'
+ name: Galeon
+ version: '$1'
+
+#Google Earth
+- regex: 'Google Earth/(\d+\.\d+)'
+ name: Google Earth
+ version: '$1'
+
+#HotJava
+- regex: 'HotJava/(\d+\.\d+)'
+ name: HotJava
+ version: '$1'
+
+#IBrowse
+- regex: 'IBrowse[ /](\d+\.\d+)'
+ name: IBrowse
+ version: '$1'
+
+#iCab
+- regex: 'iCab[ /](\d+\.\d+)'
+ name: iCab
+ version: '$1'
+
+#Internet Explorer
+- regex: 'IEMobile[ /](\d+\.\d+)'
+ name: IE Mobile
+ version: '$1'
+- regex: 'MSIE (\d+\.\d+).*XBLWP7'
+ name: IE Mobile
+ version: '$1'
+- regex: 'MSIE (\d+\.\d+)'
+ name: Internet Explorer
+ version: '$1'
+
+#Iron
+- regex: 'Iron/(\d+\.\d+)'
+ name: Iron
+ version: '$1'
+
+#Kapiko
+- regex: 'Kapiko/(\d+\.\d+)'
+ name: Kapiko
+ version: '$1'
+
+#Kazehakase
+- regex: 'Kazehakase/(\d+\.\d+)'
+ name: Kazehakase
+ version: '$1'
+
+#Kindle Browser
+- regex: 'Kindle/(\d+\.\d+)'
+ name: Kindle Browser
+ version: '$1'
+
+#K-meleon
+- regex: 'K-meleon/(\d+\.\d+)'
+ name: K-meleon
+ version: '$1'
+
+#Lightning
+- regex: 'Lightning/(\d+\.\d+)'
+ name: Lightning
+ version: '$1'
+
+#Links
+- regex: 'Links \((\d+\.\d+)'
+ name: Links
+ version: '$1'
+
+#Maxthon
+- regex: 'Maxthon (\d+\.\d+)'
+ name: Maxthon
+ version: '$1'
+- regex: '(?:Maxthon|MyIE2|Uzbl|Shiira)'
+ name: Maxthon
+ version: ''
+
+#Openwave Mobile Browser
+- regex: 'UP.Browser/(\d+\.\d+)'
+ name: Openwave Mobile Browser
+ version: '$1'
+
+#OmniWeb
+- regex: 'OmniWeb/[v]?(\d+\.\d+)'
+ name: OmniWeb
+ version: '$1'
+
+#Phoenix
+- regex: 'Phoenix/(\d+\.\d+)'
+ name: Phoenix
+ version: '$1'
+
+#Mobile Silk
+- regex: 'Silk/(\d+\.\d+)'
+ name: Mobile Silk
+ version: '$1'
+
+#Nokia Browser
+- regex: '(?:NokiaBrowser|BrowserNG)/(\d+\.\d+)'
+ name: Nokia Browser
+ version: '$1'
+- regex: 'Series60/5\.0'
+ name: Nokia Browser
+ version: '7.0'
+- regex: 'Series60/(\d+\.\d+)'
+ name: Nokia OSS Browser
+ version: '$1'
+- regex: 'S40OviBrowser/(\d+\.\d+)'
+ name: Nokia Ovi Browser
+ version: '$1'
+- regex: '^Nokia|Nokia[EN]?\d+'
+ name: Nokia Browser
+ version: ''
+
+#NetFront
+- regex: 'NetFrontLifeBrowser/(\d+\.\d+)'
+ name: NetFront Life
+ version: '$1'
+- regex: 'NetFront/(\d+\.\d+)'
+ name: NetFront
+ version: '$1'
+- regex: 'PLAYSTATION|NINTENDO 3|AppleWebKit.+ NX/\d+\.\d+\.\d+'
+ name: NetFront
+ version: ''
+
+#Obigo
+- regex: 'Obigo[ ]?(?:InternetBrowser|Browser)?[ /]([A-Za-z0-9]*)'
+ name: Obigo
+ version: '$1'
+- regex: 'Obigo|Teleca'
+ name: Obigo
+ version: ''
+
+#Palm Blazer
+- regex: 'Blazer/(\d+\.\d+)'
+ name: Palm Blazer
+ version: '$1'
+- regex: 'Pre/(\d+\.\d+)'
+ name: Palm Pre
+ version: '$1'
+
+#Polaris
+- regex: '(?:Polaris|Embider)/(\d+\.\d+)'
+ name: Polaris
+ version: '$1'
+
+#Snowshoe
+- regex: 'Snowshoe/(\d+\.\d+)'
+ name: Snowshoe
+ version: '$1'
+
+#Safari
+- regex: '(?:iPod|iPad|iPhone).+Version/(\d+\.\d+)'
+ name: Mobile Safari
+ version: '$1'
+- regex: 'Version/(\d+\.\d+).*Mobile.*Safari/'
+ name: Mobile Safari
+ version: '$1'
+- regex: '(?:iPod|iPhone|iPad)'
+ name: Mobile Safari
+ version: ''
+- regex: 'Version/(\d+\.\d+).*Safari/|Safari/\d+'
+ name: Safari
+ version: '$1'
+
+#Android Browser
+- regex: 'Android'
+ name: Android Browser
+ version: '' \ No newline at end of file
diff --git a/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/mobiles.yml b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/mobiles.yml
new file mode 100644
index 0000000000..d9a2a22f88
--- /dev/null
+++ b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/mobiles.yml
@@ -0,0 +1,958 @@
+###############
+# Piwik - Open source web analytics
+#
+# @link http://piwik.org
+# @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+#
+# @category Piwik_Plugins
+# @package Piwik_DevicesDetection
+###############
+
+# HTC
+HTC:
+ regex: 'HTC|Sprint APA|ADR[A-Za-z0-9]+'
+ device: 'smartphone'
+ models:
+ - regex: 'HTC ([A-Za-z0-9]+) Build'
+ model: '$1'
+ - regex: 'HTC ([A-Za-z0-9]+(?: [A-Za-z0-9]+)?)'
+ model: '$1'
+ - regex: 'USCCHTC(\d+)'
+ model: '$1'
+ - regex: 'Sprint APA(9292)'
+ model: '$1 (Sprint)'
+ - regex: 'HTC_([A-Za-z0-9_]+)'
+ model: '$1'
+ - regex: 'HTC(?:[\-/ ])?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'HTC;(?: )?([A-Za-z0-9 ]+)'
+ model: '$1'
+ - regex: '(ADR[A-Za-z0-9]+)'
+ model: '$1'
+
+# Tesla Model S
+Tesla:
+ regex: 'QtCarBrowser'
+ device: 'car browser'
+ model: 'Model S'
+
+# Kindle
+Kindle:
+ regex: 'KF(?:OT|TT|JWI|JWA) Build|Kindle|Silk/(\d+)\.(\d+)'
+ device: 'tablet'
+ models:
+ - regex: 'KFOT|Kindle Fire|Silk/(\d+)\.(\d+)'
+ model: 'Fire'
+ - regex: 'KFTT'
+ model: 'Fire HD'
+ - regex: 'KFJWI'
+ model: 'Fire HD 8.9" WiFi'
+ - regex: 'KFJWA'
+ model: 'Fire HD 8.9" 4G'
+
+# NOKIA
+Nokia:
+ regex: 'Nokia|Lumia|Maemo RX|portalmmm/2\.0 N7|portalmmm/2\.0 NK|nok[0-9]+|Symbian.*\s([a-zA-Z0-9]+)$'
+ device: 'smartphone'
+ models:
+ - regex: 'NokiaInternal|Nokia-WAP-Toolkit|Nokia-MIT-Browser|Nokia Mobile|Nokia Browser|Nokia/Series'
+ model: ''
+ - regex: 'Nokia(N[0-9]+)'
+ model: '$1'
+ - regex: 'Nokia-([A-Za-z0-9]+)'
+ model: 'N$1'
+ - regex: 'NOKIA; ([A-Za-z0-9\- ]+)'
+ model: '$1'
+ - regex: 'NOKIA[ ]?([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'NOKIA/([A-Za-z0-9 ]+)'
+ model: '$1'
+ - regex: '(Lumia [A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'Maemo RX-51 ([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'Maemo RX-34'
+ model: 'N800'
+ - regex: 'portalmmm/2\.0 (N7[37]|NK[A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'nok([0-9]+)'
+ model: '$1'
+ - regex: 'Symbian.*\s([a-zA-Z0-9]+)$'
+ device: 'feature phone'
+ model: '$1'
+
+# RIM/BlackBerry
+RIM:
+ regex: 'BB10;|BlackBerry|rim[0-9]+|PlayBook'
+ device: 'smartphone'
+
+ models:
+ - regex: 'BB10; ([A-Za-z0-9\- ]+)\)'
+ model: 'BlackBerry $1'
+ - regex: 'PlayBook.+RIM Tablet OS'
+ model: 'BlackBerry Playbook'
+ device: 'tablet'
+ - regex: 'BlackBerry(?: )?([A-Za-z0-9]+)'
+ model: 'BlackBerry $1'
+ - regex: 'rim([0-9]+)'
+ model: 'BlackBerry $1'
+ - regex: 'BlackBerry'
+ model: 'BlackBerry'
+
+# PALM
+Palm:
+ regex: '(?:Pre|Pixi)/(\d+)\.(\d+)|Palm|Treo'
+ device: 'smartphone'
+ models:
+ - regex: '((?:Pre|Pixi))/(\d+\.\d+)'
+ model: '$1 $2'
+ - regex: 'Palm(?: )?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'Treo([A-Za-z0-9]+)'
+ model: 'Treo $1'
+
+# HP
+HP:
+ regex: 'Touch[Pp]ad|hp-tablet|HP(?: )?iPAQ|webOS.*(P160U)'
+ device: 'smartphone'
+ models:
+ - regex: 'Touch[Pp]ad/(\d+\.\d+)|hp-tablet'
+ model: 'TouchPad'
+ device: 'tablet'
+ - regex: 'HP(?: )?iPAQ(?: )?([A-Za-z0-9]+)'
+ model: 'iPAQ $1'
+ - regex: 'webOS.*(P160U)'
+ model: 'Veer'
+
+# TiPhone
+TiPhone:
+ regex: 'TiPhone(?: )?([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Apple
+Apple:
+ regex: 'AppleTV|iPad|iPod|iPhone'
+ models:
+ - regex: 'AppleTV'
+ model: 'Apple TV'
+ device: 'tv'
+ - regex: 'iPad'
+ model: 'iPad'
+ device: 'tablet'
+ - regex: 'iPod'
+ model: 'iPod Touch'
+ device: 'palmtop'
+ - regex: 'iPhone'
+ model: 'iPhone'
+ device: 'smartphone'
+
+# Acer
+Acer:
+ regex: 'acer[\-_]([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Airness
+Airness:
+ regex: 'AIRNESS-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Alcatel
+Alcatel:
+ regex: 'Alcatel|Alc([A-Za-z0-9]+)'
+ device: 'smartphone'
+ models:
+ - regex: 'Alcatel UP'
+ model: ''
+ - regex: 'ALCATEL[ \-]([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'ALCATEL_([A-Za-z0-9_]+)'
+ model: '$1'
+ - regex: 'Alc([A-Za-z0-9]+)'
+ model: '$1'
+
+# Amoi
+Amoi:
+ regex: 'Amoi'
+ device: 'smartphone'
+ models:
+ - regex: 'Amoi[\- /](A-Za-z0-9]+)'
+ mobile: '$1'
+ - regex: 'Amoisonic-([A-Za-z0-9]+)'
+ model: '$1'
+
+# Asus
+Asus:
+ regex: 'Asus'
+ device: 'smartphone'
+ models:
+ - regex: 'Asus(?:-|;)?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'ASUS (Transformer Pad TF300T)'
+ device: 'tablet'
+ model: '$1'
+
+# Audiovox
+Audiovox:
+ regex: 'Audiovox|CDM|UTS(?:TARCOM)?\-|audio([A-Za-z0-9\-]+)'
+ device: 'smartphone'
+ models:
+ - regex: 'Audiovox[_\-]([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'CDM(?:-)?([A-Za-z0-9]+)'
+ model: 'CDM-$1'
+ - regex: 'UTS(?:TARCOM)?-([A-Za-z0-9\-]+)'
+ model: 'CDM-$1'
+ - regex: 'audio([A-Za-z0-9\-]+)'
+ model: 'CDM-$1'
+
+# Avvio
+Avvio:
+ regex: 'Avvio[ _]([A-Za-z0-9\-]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Bird
+Bird:
+ regex: 'BIRD[\-. _]([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Becker
+Becker:
+ regex: 'Becker-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Beetel
+Beetel:
+ regex: 'Beetel ([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# BenQ-Siemens
+BenQ-Siemens:
+ regex: 'BENQ-SIEMENS - ([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# BenQ
+BenQ:
+ regex: 'BENQ(?:[ \-])?([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Capitel
+Capitel:
+ regex: 'Capitel-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Compal
+Compal:
+ regex: 'Compal-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Cricket
+Cricket:
+ regex: 'Cricket-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Dell
+Dell:
+ regex: 'Dell ([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Dbtel
+Dbtel:
+ regex: 'DBTEL(?:[\-/])?([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Dicam
+Dicam:
+ regex: 'DICAM-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# DoCoMo
+DoCoMo:
+ regex: 'DoCoMo|\;FOMA|KGT/1\.0'
+ device: 'feature phone'
+ models:
+ - regex: 'DoCoMo/[12]\.0[/ ]([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: '([A-Za-z0-9]+)(?:_W)?\;FOMA'
+ model: '$1'
+ - regex: 'KGT/1\.0 ([A-Za-z0-9]+)'
+ model: '$1'
+
+# Dopod
+Dopod:
+ regex: 'Dopod(?: )?([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Ericy
+Ericy:
+ regex: 'Ericy-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Sony Ericsson
+Sony Ericsson:
+ regex: 'Sony(?: )?Ericsson|portalmmm/2\.0 K'
+ device: 'smartphone'
+ models:
+ - regex: 'SonyEricsson([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'Sony(?: )?Ericsson ([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'portalmmm/2.0 K([A-Za-z0-9]+)'
+ model: 'K$1'
+
+# Ericsson
+Ericsson:
+ regex: 'Ericsson(?:/ )?([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# eTouch
+eTouch:
+ regex: 'eTouch(?: )?([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Ezze
+Ezze:
+ regex: 'EZZE-|EZ([A-Za-z0-9]+)'
+ device: 'feature phone'
+ models:
+ - regex: 'EZZE-([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'EZ([A-Za-z0-9]+)'
+ model: 'EZ$1'
+
+# Ezio
+Ezio:
+ regex: 'EZIO-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Gionee
+Gionee:
+ regex: 'GIONEE-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Google
+Google:
+ regex: 'Nexus|GoogleTV'
+ device: 'smartphone'
+ models:
+ - regex: '(Galaxy Nexus)'
+ model: '$1'
+ - regex: '(Nexus (:?S|4|One))'
+ model: '$1'
+ - regex: '(Nexus (:?7|10))'
+ device: 'tablet'
+ model: '$1'
+ - regex: '(GoogleTV)'
+ device: 'tv'
+ model: '$1'
+
+# Gradiente
+Gradiente:
+ regex: 'GRADIENTE'
+ device: 'feature phone'
+ models:
+ - regex: 'GRADIENTE-([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'GRADIENTE ([A-Za-z0-9\-]+)'
+ model: '$1'
+
+# Grundig
+Grundig:
+ regex: 'GRUNDIG|portalmmm/2\.0 G'
+ device: 'tv'
+ models:
+ - regex: 'GRUNDIG ([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'portalmmm/2\.0 G([A-Za-z0-9]+)'
+ model: 'G$1'
+
+# Haier
+Haier:
+ regex: 'Haier[ -]([A-Za-z0-9\-]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Huawei
+Huawei:
+ regex: 'Huawei|vodafone([A-Za-z0-9]+)'
+ device: 'smartphone'
+ models:
+ - regex: 'Huawei(?:[\- /_]|/1\.0/)?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'vodafone([A-Za-z0-9]+)'
+ model: 'Vodafone $1'
+
+# Innostream
+Innostream:
+ regex: 'INNO([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: 'INNO$1'
+
+# Inq
+INQ:
+ regex: 'INQ/([A-Za-z0-9\-]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# i-mate
+i-mate:
+ regex: 'i-mate ([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# i-mobile
+i-mobile:
+ regex: 'i-mobile(?: )?([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# ikomo
+iKoMo:
+ regex: 'iKoMo ([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# kddi
+KDDI:
+ regex: 'kddi-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# kyocera
+Kyocera:
+ regex: 'Kyocera|KWC-|QC-'
+ device: 'smartphone'
+ models:
+ - regex: 'Kyocera-KZ-([A-Za-z0-9]+)'
+ model: 'KZ $1'
+ - regex: 'Kyocera(:?[\-/])?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: '(?:KWC|QC)-([A-Za-z0-9]+)'
+ model: '$1'
+
+# lanix
+Lanix:
+ regex: 'LANIX-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# lct
+LCT:
+ regex: 'LCT_([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# lenovo
+Lenovo:
+ regex: 'Lenovo[\-_]([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# lguplus
+LGUPlus:
+ regex: 'LGUPlus'
+ device: 'smartphone'
+ model: ''
+
+# lg
+LG:
+ regex: 'LG|portalmmm/2\.0 (?:KE|KG|KP|L3)|VX[0-9]+'
+ device: 'smartphone'
+ models:
+ - regex: 'LGE_DLNA_SDK'
+ device: 'tv'
+ model: 'NetCast'
+ - regex: 'LGE(?: |-LG| LG-AX|-)([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'LGE;([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'LG(?:/|-LG| |-)?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'LG; ([A-Za-z0-9 ]+)'
+ model: '$1'
+ - regex: 'portalmmm/2.0 ((?:KE|KG|KP|L3)[A-Za-z0-9]+)'
+ model: '$1'
+ - regex: '(VX[0-9]+)'
+ model: '$1'
+
+# microsoft
+Microsoft:
+ regex: 'Xbox|KIN\.(?:One|Two)'
+ device: 'console'
+ model: 'Xbox 360'
+
+# Konka
+Konka:
+ regex: 'KONKA_([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Karbonn
+Karbonn:
+ regex: 'Karbonn_([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Sagem
+Sagem:
+ regex: 'SAGEM|portalmmm/2.0 (?:SG|my)'
+ device: 'smartphone'
+ models:
+ - regex: 'SAGEM ([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'SAGEM-([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'portalmmm/2.0 ((?:SG|my)[A-Za-z0-9]+)'
+ model: '$1'
+
+# micromax
+MicroMax:
+ regex: 'MicroMax(?:[ \-])?([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# mio
+Mio:
+ regex: 'MIO(?:/)?([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# mitsubishi
+Mitsubishi:
+ regex: 'MITSU|portalmmm/[12]\.0 M'
+ device: 'feature phone'
+ models:
+ - regex: 'MITSU/[A-Za-z0-9.]+ \(([A-Za-z0-9]+)\)'
+ model: '$1'
+ - regex: 'MITSU[ \-]?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'portalmmm/[12]\.0 (M[A-Za-z0-9]+)'
+ model: '$1'
+
+# motorola
+Motorola:
+ regex: 'MOT|(?<!AN)DROID (?:Build|([A-Za-z0-9]+))|portalmmm/2.0 (?:E378i|L6|L7|v3)'
+ device: 'smartphone'
+ models:
+ - regex: 'Motorola[ \-]([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'MOTORAZR[ \-]([A-Za-z0-9]+)'
+ model: 'RAZR $1'
+ - regex: 'MOTORIZR[ \-]([A-Za-z0-9]+)'
+ model: 'RIZR $1'
+ - regex: 'MOT[O]?[\-]?([A-Za-z0-9.]+)'
+ model: '$1'
+ - regex: '(?<!AN)DROID (?:Build|([A-Za-z0-9]+))'
+ model: 'DROID $1'
+ - regex: 'portalmmm/2.0 ((?:E378i|L6|L7|V3)[A-Za-z0-9]+)'
+ model: '$1'
+
+# myphone
+MyPhone:
+ regex: 'MyPhone([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# nec
+NEC:
+ regex: 'NEC|KGT/2\.0|portalmmm/1\.0 (?:DB|N)|(?:portalmmm|o2imode)/2.0[ ,]N'
+ device: 'smartphone'
+ models:
+ - regex: '(?:NEC-|KGT/2\.0 )([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'portalmmm/1\.0 ((?:DB|N)[A-Za-z0-9]+)'
+ model: '$1'
+ - regex: '(?:portalmmm|o2imode)/2\.0[ ,](N[A-Za-z0-9]+)'
+ model: '$1'
+
+# newgen
+Newgen:
+ regex: 'NEWGEN\-([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# nintendo
+Nintendo:
+ regex: 'Nintendo (([3]?DS[i]?)|Wii[U]?)'
+ device: 'console'
+ model: '$1'
+
+# ngm
+NGM:
+ regex: 'NGM_([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# nexian
+Nexian:
+ regex: 'Nexian'
+ device: 'smartphone'
+ models:
+ - regex: 'Nexian[ ]?([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'Nexian-([A-Za-z0-9]+)'
+ model: '$1'
+
+# o2
+O2:
+ regex: 'Xda|O2[ \-]|COCOON'
+ device: 'smartphone'
+ models:
+ - regex: '(Xda[ _][A-Za-z0-9_]+)'
+ models: '$1'
+ - regex: '(COCOON)'
+ models: '$1'
+ - regex: 'O2 ([A-Za-z0-9 ]+)'
+ models: '$1'
+ - regex: 'O2-([A-Za-z0-9]+)'
+ models: '$1'
+
+# onda
+Onda:
+ regex: 'Onda'
+ device: 'smartphone'
+ models:
+ regex: '([A-Za-z0-9]+)[ _]Onda'
+ model: '$1'
+ regex: 'Onda ([A-Za-z0-9]+)'
+ model: '$1'
+
+# oppo
+OPPO:
+ regex: 'OPPO[ ]?([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# orange
+Orange:
+ regex: 'SPV[ \-]?([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: 'SPV $1'
+
+# panasonic
+Panasonic:
+ regex: 'Panasonic'
+ device: 'smartphone'
+ models:
+ - regex: 'Panasonic MIL DLNA'
+ device: 'tv'
+ model: 'Viera Cast'
+ - regex: 'Panasonic[ \-]?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'portalmmm/2.0 (P[A-Za-z0-9]+)'
+ model: '$1'
+
+# philips
+Philips:
+ regex: 'Philips'
+ device: 'smartphone'
+ models:
+ - regex: 'Philips-FISIO ([A-Za-z0-9]+)'
+ model: 'Fisio $1'
+ - regex: 'Philips[ ]?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'Philips-([A-Za-z0-9\-@]+)'
+ model: '$1'
+
+# phoneOne
+phoneOne:
+ regex: 'phoneOne[ \-]?([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Rover
+Rover:
+ regex: 'Rover ([0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Siemens
+Siemens:
+ regex: 'SIEMENS|SIE-|portalmmm/2\.0 SI|S55|SL45i'
+ device: 'smartphone'
+ models:
+ - regex: 'SIEMENS[ \-]([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'SIE(?:MENS )?[\-]?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: '(S55|SL45i)'
+ model: '$1'
+ - regex: 'portalmmm/2.0 (SI[A-Za-z0-9]+)'
+ model: '$1'
+
+# Samsung
+Samsung:
+ regex: 'SAMSUNG|S(?:CH|GH|PH|EC|AM)-|SMART-TV|GT-|Galaxy|(?:portalmmm|o2imode)/2\.0 [SZ]|sam[rua]'
+ device: 'smartphone'
+ models:
+ - regex: 'SAMSUNG[\-;][ ]?([A-Za-z0-9]+[\-_][A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'SAMSUNG[ _/]?([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'SAMSUNG;[ ]?([A-Za-z0-9 ]+)'
+ model: '$1'
+ - regex: '((?:SCH|SGH|SPH|GT)-[A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'SEC-([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'SAM-([A-Za-z0-9]+)'
+ model: 'SCH-$1'
+ - regex: 'SMART-TV'
+ device: 'tv'
+ model: 'Smart TV'
+ - regex: '(Galaxy [A-Za-z0-9]+)'
+ model: '$1'
+ - regex: '(?:portalmmm|o2imode)/2\.0 ([SZ][A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'sam([rua][0-9]+)'
+ model: 'SCH-$1'
+
+# pantech
+Pantech:
+ regex: 'Pantech|P[GTN]-|TX[T]?[0-9]+'
+ device: 'smartphone'
+ models:
+ - regex: 'Pantech[ \-]?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'Pantech_([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: '(P[GTN]-[A-Za-z0-9]+)'
+ model: '$1'
+ - regex: '(TX[T]?[0-9]+)'
+ model: '$1'
+
+# Sanyo
+Sanyo:
+ regex: 'Sanyo|MobilePhone '
+ device: 'smartphone'
+ models:
+ - regex: 'SANYO[ \-_]([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'MobilePhone ([A-Za-z0-9\-]+)'
+ model: '$1'
+
+# Sega
+Sega:
+ regex: 'Dreamcast'
+ device: 'console'
+ model: 'Dreamcast'
+
+# Sendo
+Sendo:
+ regex: 'Sendo([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Spice
+Spice:
+ regex: 'Spice'
+ device: 'smartphone'
+ models:
+ - regex: 'Spice ([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'Spice-([A-Za-z0-9]+)'
+ model: '$1'
+
+# Sharp
+Sharp:
+ regex: 'SHARP|SBM'
+ device: 'smartphone'
+ models:
+ - regex: 'SHARP-AQUOS'
+ device: 'tv'
+ model: 'Aquos Net Plus'
+ - regex: 'SHARP[ \-]([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: '(?:SHARP|SBM)([A-Za-z0-9]+)'
+ model: '$1'
+
+# Softbank
+Softbank:
+ regex: 'Softbank|J-PHONE'
+ device: 'smartphone'
+ models:
+ - regex: 'Softbank/[12]\.0/([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: '([A-Za-z0-9]+);Softbank;'
+ model: '$1'
+ - regex: 'J-PHONE/[0-9]\.[0-9]/([A-Za-z0-9\-]+)'
+ model: '$1'
+
+# Sony
+Sony:
+ regex: 'Sony|PlayStation'
+ device: 'smartphone'
+ models:
+ - regex: 'Sony[ ]?([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: '(PlayStation (?:3|Portable|Vita))'
+ device: 'console'
+ model: '$1'
+
+# Qtek
+Qtek:
+ regex: 'Qtek[ _]?([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# T-Mobile
+T-Mobile:
+ regex: 'T-Mobile[ _]([A-Za-z0-9 ]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Tcl
+TCL:
+ regex: 'TCL-([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Telit
+Telit:
+ regex: 'Telit'
+ device: 'feature phone'
+ models:
+ - regex: 'Telit_Mobile_Terminals-([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'Telit[\-_]?([A-Za-z0-9]+)'
+ model: '$1'
+
+# Tianyu
+TIANYU:
+ regex: 'TIANYU'
+ device: 'feature phone'
+ models:
+ - regex: 'TIANYU ([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'TIANYU-KTOUCH/([A-Za-z0-9]+)'
+ model: '$1'
+
+# Toplux
+Toplux:
+ regex: 'Toplux ([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# UTStarcom
+UTStarcom:
+ regex: 'utstar([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Vitelcom
+Vitelcom:
+ regex: 'Vitelcom|portalmmm/[12].0 TSM'
+ device: 'feature phone'
+ models:
+ - regex: 'TSM-([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'TSM([A-Za-z0-9\-]+)'
+ model: '$1'
+ - regex: 'portalmmm/[12].0 (TSM[A-Za-z0-9 ]+)'
+ model: '$1'
+
+# VK Mobile
+VK Mobile:
+ regex: 'VK[\-]?([A-Za-z0-9 ]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Vertu
+Vertu:
+ regex: 'Vertu[ ]?([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Videocon
+Videocon:
+ regex: 'Videocon_([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Voxtel
+Voxtel:
+ regex: 'Voxtel_([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Wellcom
+WellcoM:
+ regex: 'WELLCOM[ _\-/]([A-Za-z0-9]+)'
+ device: 'smartphone'
+ model: '$1'
+
+# Wonu
+Wonu:
+ regex: 'Wonu ([A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Zonda
+Zonda:
+ regex: '(ZM(?:CK|EM|TFTV|TN)[A-Za-z0-9]+)'
+ device: 'feature phone'
+ model: '$1'
+
+# Toshiba
+Toshiba:
+ regex: 'Toshiba|portalmmm/[12].0 TS'
+ device: 'smartphone'
+ models:
+ - regex: 'Toshiba[ /_\-]?([A-Za-z0-9 ]+)'
+ model: '$1'
+ - regex: 'portalmmm/[12].0 (TS[A-Za-z0-9 ]+)'
+ model: '$1'
+
+# Fly
+Fly:
+ regex: 'Fly|MERIDIAN-'
+ device: 'smartphone'
+ models:
+ - regex: 'Fly[ _\-]?([A-Za-z0-9]+)'
+ model: '$1'
+ - regex: 'MERIDIAN-([A-Za-z0-9]+)'
+ model: '$1'
+
+# WebTV
+WebTV:
+ regex: 'WebTV/(\d+\.\d+)'
+ device: 'tv'
+ model: '$1'
+
+# ZTE
+ZTE:
+ regex: 'ZTE|Z331'
+ device: 'smartphone'
+ models:
+ - regex: '(Z331)'
+ model: '$1'
+ - regex: 'ZTE-(?:G |G-)?([A-Za-z0-9 _]+)'
+ model: '$1'
+ - regex: 'ZTE ([A-Za-z0-9]+)'
+ model: '$1'
+
+# Symbian to Nokia ??
+# Change name from Nokia to other to not change above Nokia element
+#Nokia:
+# regex: 'Symbian'
+# device: 'feature phone' \ No newline at end of file
diff --git a/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/oss.yml b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/oss.yml
new file mode 100644
index 0000000000..71b85f987f
--- /dev/null
+++ b/plugins/DevicesDetection/UserAgentParserEnhanced/regexes/oss.yml
@@ -0,0 +1,427 @@
+###############
+# Piwik - Open source web analytics
+#
+# @link http://piwik.org
+# @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+#
+# @category Piwik_Plugins
+# @package Piwik_DevicesDetection
+###############
+
+##########
+# Tizen
+##########
+- regex: 'Tizen'
+ name: 'Tizen'
+ version: ''
+
+
+
+##########
+# Android
+##########
+- regex: 'Android[ /](\d+\.\d+)'
+ name: 'Android'
+ version: '$1'
+
+
+- regex: 'Android|Silk-Accelerated=[a-z]{4,5}'
+ name: 'Android'
+ version: ''
+
+
+
+##########
+# Linux
+##########
+- regex: 'Linux; .*((?:Arch Linux|Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Slackware|SUSE|Puppy|CentOS|BackTrack|YunOs|Presto))[ /](\d+\.\d+)'
+ name: '$1'
+ version: '$2'
+
+
+- regex: '((?:Arch Linux|Debian|Knoppix|Mint|Ubuntu|Kubuntu|Xubuntu|Lubuntu|Fedora|Red Hat|Mandriva|Gentoo|Slackware|SUSE|Puppy|CentOS|BackTrack|YunOs|Presto));.*Linux'
+ name: '$1'
+ version: ''
+
+
+- regex: 'Linux; |Linux (?:x86_64|zbov|i686)'
+ name: 'Linux'
+ version: ''
+
+
+
+##########
+# Windows Mobile
+##########
+- regex: 'Windows Phone (?:OS)?[ ]?(\d+\.\d+)'
+ name: 'Windows Phone'
+ version: '$1'
+
+
+- regex: 'XBLWP7|Windows Phone'
+ name: 'Windows Phone'
+ version: ''
+
+- regex: 'Windows CE'
+ name: 'Windows CE'
+ version: ''
+
+
+- regex: '(?:IEMobile|Windows Mobile)(?: (\d+\.\d+))?'
+ name: 'Windows Mobile'
+ version: '$1'
+
+
+- regex: 'Windows NT 6.2; ARM;'
+ name: 'Windows RT'
+ version: ''
+
+
+
+##########
+# Windows
+##########
+- regex: 'CYGWIN_NT-6.2|Windows NT 6.2|Windows 8'
+ name: 'Windows 8'
+ version: '8'
+
+
+- regex: 'CYGWIN_NT-6.1|Windows NT 6.1|Windows 7'
+ name: 'Windows 7'
+ version: '7'
+
+
+- regex: 'CYGWIN_NT-6.0|Windows NT 6.0|Windows Vista'
+ name: 'Windows Vista'
+ version: 'Vista'
+
+
+- regex: 'CYGWIN_NT-5.2|Windows NT 5.2|Windows Server 2003 / XP x64'
+ name: 'Windows Server 2003'
+ version: 'Server 2003'
+
+
+- regex: 'CYGWIN_NT-5.1|Windows NT 5.1|Windows XP'
+ name: 'Windows XP'
+ version: 'XP'
+
+
+- regex: 'CYGWIN_NT-5.0|Windows NT 5.0|Windows 2000'
+ name: 'Windows 2000'
+ version: '2000'
+
+
+- regex: 'CYGWIN_NT-4.0|Windows NT 4.0|WinNT|Windows NT'
+ name: 'Windows NT'
+ version: 'NT'
+
+
+- regex: 'CYGWIN_ME-4.90|Win 9x 4.90|Windows ME'
+ name: 'Windows ME'
+ version: 'ME'
+
+
+- regex: 'CYGWIN_98-4.10|Win98|Windows 98'
+ name: 'Windows 98'
+ version: '98'
+
+
+- regex: 'CYGWIN_95-4.0|Win32|Win95|Windows 95|Windows_95'
+ name: 'Windows 95'
+ version: '95'
+
+
+- regex: 'Windows 3.1'
+ name: 'Windows 3.1'
+ version: '3.1'
+
+
+- regex: 'Windows'
+ name: 'Windows'
+ version: ''
+
+
+
+##########
+# Mac
+##########
+- regex: 'Mac OS X (\d+[_.]\d+)'
+ name: 'Mac'
+
+ version: '$1'
+
+- regex: 'Darwin|Macintosh|Mac_PowerPC|PPC|Mac PowerPC'
+ name: 'Mac'
+ version: ''
+
+
+
+##########
+# iOS
+##########
+- regex: '(?:CPU OS|iPhone OS) (\d+_\d+)'
+ name: 'iOS'
+
+ version: '$1'
+
+- regex: '(?:iPhone|iPad|iPod)(?:.*Mac OS X.*Version/(\d+\.\d+)|; Opera)'
+ name: 'iOS'
+ version: '$1'
+
+
+
+##########
+# webOS
+##########
+- regex: '(?:webOS|Palm webOS)(?:/(\d+\.\d+))?'
+ name: 'webOS'
+ version: '$1'
+
+
+- regex: '(?:PalmOS|Palm OS)(?:/(\d+\.\d+))?'
+ name: 'PalmOS'
+ version: ''
+
+
+
+##########
+# ChromeOS
+##########
+- regex: 'CrOS [a-z0-9_]+ (\d+\.\d+)'
+ name: 'Chrome OS'
+ version: '$1'
+
+
+
+##########
+# BlackBerry
+##########
+- regex: '(?:BB10;.+Version|Black[Bb]erry[0-9a-z]+|Black[Bb]erry.+Version)/(\d+\.\d+)'
+ name: 'BlackBerry OS'
+ version: '$1'
+
+
+- regex: 'RIM Tablet OS (\d+\.\d+)'
+ name: 'BlackBerry Tablet OS'
+ version: '$1'
+
+
+- regex: 'RIM Tablet OS|QNX|Play[Bb]ook'
+ name: 'BlackBerry Tablet OS'
+ version: ''
+
+
+- regex: 'Black[Bb]erry'
+ name: 'BlackBerry OS'
+ version: ''
+
+
+
+##########
+# Symbian
+##########
+- regex: 'Symbian[Oo][Ss]/(\d+\.\d+)'
+ name: 'Symbian OS'
+ version: '$1'
+
+
+- regex: 'Symbian/3.+NokiaBrowser/7\.3'
+ name: 'Symbian'
+ version: '^3 Anna'
+
+
+- regex: 'Symbian/3.+NokiaBrowser/7\.4'
+ name: 'Symbian'
+ version: '^3 Belle'
+
+
+- regex: 'Symbian[/]?3'
+ name: 'Symbian^3'
+ version: '^3'
+
+
+- regex: '(?:Series 60|SymbOS|S60)(?:[ /]?(\d+\.\d+|V\d+))?'
+ name: 'Symbian OS Series 60'
+ version: '$1'
+
+
+- regex: 'Series40'
+ name: 'Symbian OS Series 40'
+ version: ''
+
+
+- regex: 'MeeGo|WeTab'
+ name: 'MeeGo'
+ version: ''
+
+
+- regex: 'Symbian [Oo][Ss]|SymbOS'
+ name: 'Symbian OS'
+ version: ''
+
+
+- regex: 'Nokia'
+ name: 'Symbian'
+ version: ''
+
+
+
+##########
+# Firefox OS
+##########
+- regex: '(?:Mobile|Tablet);.+Firefox/\d+\.\d+'
+ name: 'Firefox OS'
+ version: ''
+
+
+
+##########
+# Bada
+##########
+- regex: 'bada'
+ name: 'Bada'
+ version: ''
+
+
+
+##########
+# Brew
+##########
+- regex: '(?:Brew MP|BREW|BMP)(?:[ /](\d+\.\d+))?'
+ name: 'Brew'
+ version: '$1'
+
+
+
+##########
+# Web TV
+##########
+- regex: 'GoogleTV[ /](\d+\.\d+)|GoogleTV'
+ name: 'Google TV'
+ version: '$1'
+
+
+- regex: 'AppleTV/(\d+\.\d+)'
+ name: 'Apple TV'
+ version: '$1'
+
+
+- regex: 'WebTV/(\d+\.\d+)'
+ name: 'WebTV'
+ version: '$1'
+
+
+
+##########
+# Unix
+##########
+- regex: 'SunOS'
+ name: 'Solaris'
+ version: ''
+
+
+- regex: 'AIX'
+ name: 'AIX'
+ version: ''
+
+
+- regex: 'HP-UX'
+ name: 'HP-UX'
+ version: ''
+
+
+- regex: 'FreeBSD'
+ name: 'FreeBSD'
+ version: ''
+
+
+- regex: 'NetBSD'
+ name: 'NetBSD'
+ version: ''
+
+
+- regex: 'OpenBSD'
+ name: 'OpenBSD'
+ version: ''
+
+
+- regex: 'DragonFly'
+ name: 'DragonFly'
+ version: ''
+
+
+- regex: 'Syllable'
+ name: 'Syllable'
+ version: ''
+
+
+- regex: 'IRIX'
+ name: 'IRIX'
+ version: ''
+
+
+- regex: 'OSF1'
+ name: 'OSF1'
+ version: ''
+
+
+
+##########
+# Gaming Console
+##########
+- regex: 'Nintendo Wii'
+ name: 'Nintendo'
+ version: 'Wii'
+
+
+- regex: 'PlayStation 3|PlayStation3'
+ name: 'PlayStation'
+ version: '3'
+
+
+- regex: 'Xbox|KIN\.(?:One|Two)'
+ name: 'Xbox'
+ version: '360'
+
+
+
+##########
+# Mobile Gaming Console
+##########
+- regex: 'Nitro|Nintendo ([3]?DS[i]?)'
+ name: 'Nintendo Mobile'
+ version: '$1'
+
+
+- regex: 'PlayStation ((?:Portable|Vita))'
+ name: 'PlayStation'
+ version: '$1'
+
+
+
+##########
+# IBM
+##########
+- regex: 'OS/2'
+ name: 'OS/2'
+ version: ''
+
+
+
+##########
+# Simulators
+##########
+- regex: '(Talkatone|WinWAP)'
+ name: '$1'
+ version: ''
+
+
+
+##########
+# Bot
+##########
+- regex: '(nuhk|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask Jeeves/Teoma|ia_archiver|ScoutJet|Gulper Web Bot|EmailWolf|grub-client|Download Demon|OmniWeb|SearchExpress|Microsoft URL Control|bot|borg|yahoo|slurp|msnbot|msrbot|openbot|archiver|netresearch|transcoder|crawler|lycos|scooter|altavista|teoma|gigabot|baiduspider|blitzbot|oegp|charlotte|furlbot|http%20client|polybot|htdig|ichiro|mogimogi|larbin|pompos|scrubby|searchsight|seekbot|semanticdiscovery|silk|snappy|speedy|spider|voila|vortex|voyager|zao|zeal|fast-webcrawler|converacrawler|dataparksearch|findlinksYottaaMonitor|BrowserMob|HttpMonitor|YandexBot|Slurp|BingPreview|PagePeeker|ThumbShotsBot|WebThumb|URL2PNG|ZooShot|GomezA|Catchpoint bot|Willow Internet Crawler|Google SketchUp|Read%20Later|Minimo|Pingdom.com|facebookexternalhit|Twitterbot|RackspaceBot)'
+ name: 'Bot '
+ version: ''
+
+ \ No newline at end of file
diff --git a/plugins/DevicesDetection/functions.php b/plugins/DevicesDetection/functions.php
new file mode 100644
index 0000000000..4819cd6974
--- /dev/null
+++ b/plugins/DevicesDetection/functions.php
@@ -0,0 +1,149 @@
+<?php
+
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_DevicesDetection
+ */
+function Piwik_GetBrandLogo($label)
+{
+ $path = dirname(__FILE__) . '/images/brand/' . $label . '.ico';
+ if (file_exists($path)) {
+ return 'plugins/DevicesDetection/images/brand/' . $label . '.ico';
+ } else {
+ return 'plugins/DevicesDetection/images/brand/unknown.ico';
+ }
+}
+
+function Piwik_getBrowserFamilyFullNameExtended($label)
+{
+ foreach (UserAgentParserEnhanced::$browserFamilies as $name => $family) {
+ if (in_array($label, $family)) {
+ return $name;
+ }
+ }
+ return Piwik_Translate('General_Unknown');
+}
+
+function Piwik_getBrowserFamilyLogoExtended($label)
+{
+ if (array_key_exists($label, UserAgentParserEnhanced::$browserFamilies)) {
+ $path = 'plugins/UserSettings/images/browsers/' . UserAgentParserEnhanced::$browserFamilies[$label][0] . '.gif';
+ } else {
+ $path = 'plugins/UserSettings/images/browsers/UNK.gif';
+ }
+ return $path;
+}
+
+function Piwik_getBrowserNameExtended($label)
+{
+ $short = substr($label, 0, 2);
+ $ver = substr($label, 3, 10);
+ if (array_key_exists($short, UserAgentParserEnhanced::$browsers)) {
+ return trim(ucfirst(UserAgentParserEnhanced::$browsers[$short]) . ' ' . $ver);
+ } else {
+ return Piwik_Translate('General_Unknown');
+ }
+}
+
+function Piwik_getBrowserLogoExtended($label)
+{
+ $short = substr($label, 0, 2);
+
+ $familyName = Piwik_getBrowserFamilyFullNameExtended($short);
+ $path = Piwik_getBrowserFamilyLogoExtended($familyName);
+
+ return $path;
+}
+
+function Piwik_getDeviceBrandLabel($label)
+{
+ if (array_key_exists($label, UserAgentParserEnhanced::$deviceBrands)) {
+ return ucfirst(UserAgentParserEnhanced::$deviceBrands[$label]);
+ } else {
+ return Piwik_Translate('General_Unknown');
+ }
+}
+
+function Piwik_getDeviceTypeLabel($label)
+{
+ if (isset(UserAgentParserEnhanced::$deviceTypes[$label])) {
+ return UserAgentParserEnhanced::$deviceTypes[$label];
+ } else {
+ return Piwik_Translate('General_Unknown');
+ }
+}
+
+function Piwik_getDeviceTypeLogo($label)
+{
+ $deviceTypeLogos = Array(
+ "Desktop" => "normal.gif",
+ "Smartphone" => "smartphone.png",
+ "Tablet" => "tablet.png",
+ "Tv" => "tv.png",
+ "Feature phone" => "mobile.gif",
+ "Console" => "console.gif");
+
+ if (!array_key_exists($label, $deviceTypeLogos) || $label == "Unknown") {
+ $label = 'unknown.gif';
+ } else {
+ $label = $deviceTypeLogos[$label];
+ }
+ $path = 'plugins/DevicesDetection/images/screens/' . $label;
+ return $path;
+}
+
+function Piwik_getModelName($label)
+{
+ if (!$label) {
+ return Piwik_Translate('General_Unknown');
+ }
+ return $label;
+}
+
+function Piwik_getOSFamilyFullNameExtended($label)
+{
+ foreach (UserAgentParserEnhanced::$osFamilies as $name => $family) {
+ if (in_array($label, $family)) {
+ return $name;
+ }
+ }
+ return Piwik_Translate('General_Unknown');
+}
+
+function Piwik_getOsFamilyLogoExtended($label)
+{
+ if (array_key_exists($label, UserAgentParserEnhanced::$osFamilies)) {
+ $path = 'plugins/UserSettings/images/os/' . UserAgentParserEnhanced::$osFamilies[$label][0] . ".gif";
+ } else {
+ $path = 'plugins/UserSettings/images/os/UNK.gif';
+ }
+ return $path;
+}
+
+function Piwik_getOsFullNameExtended($label)
+{
+ if (!empty($label) && $label != ";") {
+ $os = substr($label, 0, 3);
+ $ver = substr($label, 4, 15);
+ $name = UserAgentParserEnhanced::getOsNameFromId($os, $ver);
+ if(!empty($name)) {
+ return $name;
+ }
+ }
+ return Piwik_Translate('General_Unknown');
+}
+
+
+
+function Piwik_getOsLogoExtended($label)
+{
+ $short = substr($label, 0, 3);
+ $familyName = Piwik_getOsFamilyFullNameExtended($short);
+ $path = Piwik_getOsFamilyLogoExtended($familyName);
+ return $path;
+} \ No newline at end of file
diff --git a/plugins/DevicesDetection/images/brand/Acer.ico b/plugins/DevicesDetection/images/brand/Acer.ico
new file mode 100644
index 0000000000..6c5b9f6a9e
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Acer.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Alcatel.ico b/plugins/DevicesDetection/images/brand/Alcatel.ico
new file mode 100644
index 0000000000..9d8de98eab
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Alcatel.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Apple.ico b/plugins/DevicesDetection/images/brand/Apple.ico
new file mode 100644
index 0000000000..8f562cc422
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Apple.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Asus.ico b/plugins/DevicesDetection/images/brand/Asus.ico
new file mode 100644
index 0000000000..6ca527164a
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Asus.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Audiovox.ico b/plugins/DevicesDetection/images/brand/Audiovox.ico
new file mode 100644
index 0000000000..1745af087e
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Audiovox.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Avvio.ico b/plugins/DevicesDetection/images/brand/Avvio.ico
new file mode 100644
index 0000000000..e96d6e7d9c
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Avvio.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Becker.ico b/plugins/DevicesDetection/images/brand/Becker.ico
new file mode 100644
index 0000000000..414fab5a71
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Becker.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Beetel.ico b/plugins/DevicesDetection/images/brand/Beetel.ico
new file mode 100644
index 0000000000..c9bef20575
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Beetel.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/BenQ.ico b/plugins/DevicesDetection/images/brand/BenQ.ico
new file mode 100644
index 0000000000..dcfdd0908d
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/BenQ.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Compal.ico b/plugins/DevicesDetection/images/brand/Compal.ico
new file mode 100644
index 0000000000..20c1340e86
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Compal.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Cricket.ico b/plugins/DevicesDetection/images/brand/Cricket.ico
new file mode 100644
index 0000000000..cdaeffaa01
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Cricket.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Dell.ico b/plugins/DevicesDetection/images/brand/Dell.ico
new file mode 100644
index 0000000000..1d7e4ac457
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Dell.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/DoCoMo.ico b/plugins/DevicesDetection/images/brand/DoCoMo.ico
new file mode 100644
index 0000000000..7874325557
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/DoCoMo.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Ericsson.ico b/plugins/DevicesDetection/images/brand/Ericsson.ico
new file mode 100644
index 0000000000..2b91cfd581
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Ericsson.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Google.ico b/plugins/DevicesDetection/images/brand/Google.ico
new file mode 100644
index 0000000000..cae29b26fd
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Google.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Gradiente.ico b/plugins/DevicesDetection/images/brand/Gradiente.ico
new file mode 100644
index 0000000000..de530228b9
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Gradiente.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Grundig.ico b/plugins/DevicesDetection/images/brand/Grundig.ico
new file mode 100644
index 0000000000..3dd1731e0a
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Grundig.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/HP.ico b/plugins/DevicesDetection/images/brand/HP.ico
new file mode 100644
index 0000000000..6870d2a134
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/HP.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/HTC.ico b/plugins/DevicesDetection/images/brand/HTC.ico
new file mode 100644
index 0000000000..a4b722423f
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/HTC.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Haier.ico b/plugins/DevicesDetection/images/brand/Haier.ico
new file mode 100644
index 0000000000..761eba5924
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Haier.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Huawei.ico b/plugins/DevicesDetection/images/brand/Huawei.ico
new file mode 100644
index 0000000000..d2d72cc39c
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Huawei.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/INQ.ico b/plugins/DevicesDetection/images/brand/INQ.ico
new file mode 100644
index 0000000000..8317daeac5
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/INQ.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/KDDI.ico b/plugins/DevicesDetection/images/brand/KDDI.ico
new file mode 100644
index 0000000000..8ba0c412b4
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/KDDI.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Karbonn.ico b/plugins/DevicesDetection/images/brand/Karbonn.ico
new file mode 100644
index 0000000000..370ac7081c
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Karbonn.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Kindle.ico b/plugins/DevicesDetection/images/brand/Kindle.ico
new file mode 100644
index 0000000000..f1c96b70a4
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Kindle.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Kyocera.ico b/plugins/DevicesDetection/images/brand/Kyocera.ico
new file mode 100644
index 0000000000..5415b00407
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Kyocera.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/LG.ico b/plugins/DevicesDetection/images/brand/LG.ico
new file mode 100644
index 0000000000..ca03ac84c8
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/LG.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/LGUPlus.ico b/plugins/DevicesDetection/images/brand/LGUPlus.ico
new file mode 100644
index 0000000000..cf8b998ce9
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/LGUPlus.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Lanix.ico b/plugins/DevicesDetection/images/brand/Lanix.ico
new file mode 100644
index 0000000000..9736d9ef90
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Lanix.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Lenovo.ico b/plugins/DevicesDetection/images/brand/Lenovo.ico
new file mode 100644
index 0000000000..beb7c247f9
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Lenovo.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/MicroMax.ico b/plugins/DevicesDetection/images/brand/MicroMax.ico
new file mode 100644
index 0000000000..d31758326d
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/MicroMax.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Microsoft.ico b/plugins/DevicesDetection/images/brand/Microsoft.ico
new file mode 100644
index 0000000000..94d1a2b3ab
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Microsoft.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Mio.ico b/plugins/DevicesDetection/images/brand/Mio.ico
new file mode 100644
index 0000000000..16f8ef2e59
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Mio.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Mitsubishi.ico b/plugins/DevicesDetection/images/brand/Mitsubishi.ico
new file mode 100644
index 0000000000..abd920e561
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Mitsubishi.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Motorola.ico b/plugins/DevicesDetection/images/brand/Motorola.ico
new file mode 100644
index 0000000000..f388f50c9a
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Motorola.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/MyPhone.ico b/plugins/DevicesDetection/images/brand/MyPhone.ico
new file mode 100644
index 0000000000..4fccc0c0c2
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/MyPhone.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/NGM.ico b/plugins/DevicesDetection/images/brand/NGM.ico
new file mode 100644
index 0000000000..5a0fcfc1ba
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/NGM.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Nintendo.ico b/plugins/DevicesDetection/images/brand/Nintendo.ico
new file mode 100644
index 0000000000..4c949d50c5
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Nintendo.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Nokia.ico b/plugins/DevicesDetection/images/brand/Nokia.ico
new file mode 100644
index 0000000000..fe54973014
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Nokia.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/O2.ico b/plugins/DevicesDetection/images/brand/O2.ico
new file mode 100644
index 0000000000..15ef4a7a24
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/O2.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/OPPO.ico b/plugins/DevicesDetection/images/brand/OPPO.ico
new file mode 100644
index 0000000000..10af023560
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/OPPO.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Onda.ico b/plugins/DevicesDetection/images/brand/Onda.ico
new file mode 100644
index 0000000000..3bc55223ee
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Onda.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Orange.ico b/plugins/DevicesDetection/images/brand/Orange.ico
new file mode 100644
index 0000000000..55437dda38
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Orange.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Palm.ico b/plugins/DevicesDetection/images/brand/Palm.ico
new file mode 100644
index 0000000000..901c44fa3f
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Palm.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Panasonic.ico b/plugins/DevicesDetection/images/brand/Panasonic.ico
new file mode 100644
index 0000000000..00a3e9cb24
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Panasonic.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Pantech.ico b/plugins/DevicesDetection/images/brand/Pantech.ico
new file mode 100644
index 0000000000..6fc39d30cb
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Pantech.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Philips.ico b/plugins/DevicesDetection/images/brand/Philips.ico
new file mode 100644
index 0000000000..8eb97f5ecd
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Philips.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/RIM.ico b/plugins/DevicesDetection/images/brand/RIM.ico
new file mode 100644
index 0000000000..c7d1bdacc2
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/RIM.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Samsung.ico b/plugins/DevicesDetection/images/brand/Samsung.ico
new file mode 100644
index 0000000000..b7a852d6aa
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Samsung.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Sanyo.ico b/plugins/DevicesDetection/images/brand/Sanyo.ico
new file mode 100644
index 0000000000..5415b00407
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Sanyo.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Sega.ico b/plugins/DevicesDetection/images/brand/Sega.ico
new file mode 100644
index 0000000000..aa5ba9bdfd
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Sega.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Softbank.ico b/plugins/DevicesDetection/images/brand/Softbank.ico
new file mode 100644
index 0000000000..da354bfb46
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Softbank.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Sony Ericsson.ico b/plugins/DevicesDetection/images/brand/Sony Ericsson.ico
new file mode 100644
index 0000000000..776a08d5af
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Sony Ericsson.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Sony.ico b/plugins/DevicesDetection/images/brand/Sony.ico
new file mode 100644
index 0000000000..776a08d5af
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Sony.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/T-Mobile.ico b/plugins/DevicesDetection/images/brand/T-Mobile.ico
new file mode 100644
index 0000000000..0496780292
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/T-Mobile.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Telit.ico b/plugins/DevicesDetection/images/brand/Telit.ico
new file mode 100644
index 0000000000..bca28a1684
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Telit.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/TiPhone.ico b/plugins/DevicesDetection/images/brand/TiPhone.ico
new file mode 100644
index 0000000000..87a7ccd4da
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/TiPhone.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Vertu.ico b/plugins/DevicesDetection/images/brand/Vertu.ico
new file mode 100644
index 0000000000..48ff28926b
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Vertu.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Videocon.ico b/plugins/DevicesDetection/images/brand/Videocon.ico
new file mode 100644
index 0000000000..dc1bda5cc6
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Videocon.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/Zonda.ico b/plugins/DevicesDetection/images/brand/Zonda.ico
new file mode 100644
index 0000000000..eede4f8baa
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/Zonda.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/eTouch.ico b/plugins/DevicesDetection/images/brand/eTouch.ico
new file mode 100644
index 0000000000..d4487284b2
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/eTouch.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/i-mobile.ico b/plugins/DevicesDetection/images/brand/i-mobile.ico
new file mode 100644
index 0000000000..c5a9b064c2
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/i-mobile.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/brand/unknown.ico b/plugins/DevicesDetection/images/brand/unknown.ico
new file mode 100644
index 0000000000..62a489e293
--- /dev/null
+++ b/plugins/DevicesDetection/images/brand/unknown.ico
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/computer.png b/plugins/DevicesDetection/images/screens/computer.png
new file mode 100644
index 0000000000..9763689e6f
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/computer.png
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/console.gif b/plugins/DevicesDetection/images/screens/console.gif
new file mode 100644
index 0000000000..7957a9106a
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/console.gif
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/dual.gif b/plugins/DevicesDetection/images/screens/dual.gif
new file mode 100644
index 0000000000..a8cb8b2963
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/dual.gif
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/mobile.gif b/plugins/DevicesDetection/images/screens/mobile.gif
new file mode 100644
index 0000000000..814642933f
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/mobile.gif
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/normal.gif b/plugins/DevicesDetection/images/screens/normal.gif
new file mode 100644
index 0000000000..afe97e9d9f
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/normal.gif
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/smartphone.png b/plugins/DevicesDetection/images/screens/smartphone.png
new file mode 100644
index 0000000000..c5bfb31ca4
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/smartphone.png
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/tablet.png b/plugins/DevicesDetection/images/screens/tablet.png
new file mode 100644
index 0000000000..e6ac30bdd8
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/tablet.png
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/tv.png b/plugins/DevicesDetection/images/screens/tv.png
new file mode 100644
index 0000000000..fb6db07cf4
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/tv.png
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/unknown.gif b/plugins/DevicesDetection/images/screens/unknown.gif
new file mode 100644
index 0000000000..2c44083422
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/unknown.gif
Binary files differ
diff --git a/plugins/DevicesDetection/images/screens/wide.gif b/plugins/DevicesDetection/images/screens/wide.gif
new file mode 100644
index 0000000000..1b09fc529b
--- /dev/null
+++ b/plugins/DevicesDetection/images/screens/wide.gif
Binary files differ
diff --git a/plugins/DevicesDetection/lang/en.php b/plugins/DevicesDetection/lang/en.php
new file mode 100644
index 0000000000..93d4504957
--- /dev/null
+++ b/plugins/DevicesDetection/lang/en.php
@@ -0,0 +1,29 @@
+<?php
+
+$translations = array(
+ "DevicesDetection_description" => "This plugin provides extended information about mobile devices, such as Brand (manufacturer), Model (device version), better Device type detection (tv, consoles, smart phones, desktop, etc) and more. This plugin adds a new report in 'Visitors > Devices'.",
+ "DevicesDetection_submenu" => "Devices",
+ 'DevicesDetection_DevicesDetection' => "Visitor Devices",
+ // DataTable label translations for reports
+ "DevicesDetection_dataTableLabelBrands" => "Brand",
+ "DevicesDetection_dataTableLabelTypes" => "Type",
+ "DevicesDetection_dataTableLabelModels" => "Model",
+ "DevicesDetection_dataTableLabelSystemFamily" => "Operating System family",
+ "DevicesDetection_dataTableLabelSystemVersion" => "Operating System version",
+ "DevicesDetection_dataTableLabelBrowserFamily" => "Browser family",
+ "DevicesDetection_dataTableLabelBrowserVersion" => "Browser version",
+ // Title translations for reports
+ "DevicesDetection_DeviceType" => "Device types report",
+ "DevicesDetection_DeviceBrand" => "Device manufacturers report",
+ "DevicesDetection_DeviceModel" => "Device model report",
+ 'DeviceDetection_OperatingSystemVersions' => "Operating System versions",
+ 'DeviceDetection_OperatingSystemFamilies' => "Operating System families",
+ 'DevicesDetection_BrowsersFamily' => 'Browsers families',
+ 'DevicesDetection_BrowserVersions' => 'Browser versions',
+ // Evolution graph title translations
+ "DevicesDetection_DeviceType" => "Device type",
+ 'DevicesDetection_DeviceBrand' => 'Device brand',
+ 'DevicesDetection_DeviceModel' => 'Device model',
+// 'DevicesDetection_OS' => 'Device operating system',
+// 'DevicesDetection_Browser' => 'Device browser',
+);
diff --git a/plugins/DevicesDetection/templates/index.tpl b/plugins/DevicesDetection/templates/index.tpl
new file mode 100644
index 0000000000..74e6c0b116
--- /dev/null
+++ b/plugins/DevicesDetection/templates/index.tpl
@@ -0,0 +1,15 @@
+<div id='leftcolumn'>
+ <h2>{"DevicesDetection_DeviceType"|Piwik_Translate}</h2>
+ {$deviceTypes}
+ <h2>{"DevicesDetection_DeviceBrand"|Piwik_Translate}</h2>
+ {$deviceBrands}
+ <h2>{"DevicesDetection_DeviceModel"|Piwik_Translate}</h2>
+ {$deviceModels}
+</div>
+
+<div id='rightcolumn'>
+ <h2>{"DeviceDetection_OperatingSystemFamilies"|Piwik_Translate}</h2>
+ {$osReport}
+ <h2>{"DevicesDetection_BrowsersFamily"|Piwik_Translate}</h2>
+ {$browserReport}
+</div>
diff --git a/plugins/ExampleUI/API.php b/plugins/ExampleUI/API.php
index 91e29806be..697844a589 100644
--- a/plugins/ExampleUI/API.php
+++ b/plugins/ExampleUI/API.php
@@ -50,11 +50,7 @@ class Piwik_ExampleUI_API
$value = array('server1' => $server1, 'server2' => $server2);
$temperatures[$subPeriod->getLocalizedShortString()] = $value;
}
-
- // convert this array to a DataTable object
- $dataTable = new Piwik_DataTable();
- $dataTable->addRowsFromArrayWithIndexLabel($temperatures);
- return $dataTable;
+ return Piwik_DataTable::makeFromIndexedArray($temperatures);
}
// we generate an array of random server temperatures
@@ -71,10 +67,7 @@ class Piwik_ExampleUI_API
$temperatures[$xAxisLabel] = $temperatureValues[$i];
}
- // convert this array to a DataTable object
- $dataTable = new Piwik_DataTable();
- $dataTable->addRowsFromArrayWithIndexLabel($temperatures);
- return $dataTable;
+ return Piwik_DataTable::makeFromIndexedArray($temperatures);
}
public function getPlanetRatios()
@@ -90,9 +83,7 @@ class Piwik_ExampleUI_API
'Neptune' => 3.883,
);
// convert this array to a DataTable object
- $dataTable = new Piwik_DataTable();
- $dataTable->addRowsFromArrayWithIndexLabel($planetRatios);
- return $dataTable;
+ return Piwik_DataTable::makeFromIndexedArray($planetRatios);
}
public function getPlanetRatiosWithLogos()
diff --git a/plugins/Feedback/Feedback.php b/plugins/Feedback/Feedback.php
index bb43437905..d514245194 100644
--- a/plugins/Feedback/Feedback.php
+++ b/plugins/Feedback/Feedback.php
@@ -38,7 +38,7 @@ class Piwik_Feedback extends Piwik_Plugin
{
Piwik_AddTopMenu(
'General_GiveUsYourFeedback',
- array('module' => 'Feedback', 'action' => 'index'),
+ array('module' => 'Feedback', 'action' => 'index', 'segment' => false),
true,
$order = 20,
$isHTML = false,
diff --git a/plugins/Feedback/javascripts/feedback.js b/plugins/Feedback/javascripts/feedback.js
index 5f7112cf8f..f99464c5bd 100644
--- a/plugins/Feedback/javascripts/feedback.js
+++ b/plugins/Feedback/javascripts/feedback.js
@@ -10,7 +10,7 @@ $(function () {
if (feedback.size()) {
var fbDiv = $('<div id="feedback-dialog"></div>').appendTo('body');
- $('a#topmenu-feedback').click(function () {
+ feedback.click(function () {
if (fbDiv.html() == '') {
fbDiv.html('<div id="feedback-loading"><img alt="" src="plugins/Zeitgeist/images/loading-blue.gif"> ' + _pk_translate('General_Loading_js') + '</div>');
}
diff --git a/plugins/Feedback/templates/index.twig b/plugins/Feedback/templates/index.twig
index 7bd23e21b5..ab26930b98 100644
--- a/plugins/Feedback/templates/index.twig
+++ b/plugins/Feedback/templates/index.twig
@@ -13,7 +13,7 @@
});
$('#feedback-form-submit').click(function () {
- var feedback = $('#feedback-form form');
+ var feedback = $('#feedback-form').find('form');
$('#feedback-form').hide();
$.post(feedback.attr('action'), feedback.serialize(), function (data) {
$('#feedback-sent').show().html(data);
diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php
index aea16ad70e..35563a7ed5 100644
--- a/plugins/Goals/API.php
+++ b/plugins/Goals/API.php
@@ -206,18 +206,19 @@ class Piwik_Goals_API
Piwik::checkUserHasViewAccess($idSite);
$recordNameFinal = $recordName;
if ($abandonedCarts) {
- $recordNameFinal = Piwik_Goals::getItemRecordNameAbandonedCart($recordName);
+ $recordNameFinal = Piwik_Goals_Archiver::getItemRecordNameAbandonedCart($recordName);
}
$archive = Piwik_Archive::build($idSite, $period, $date);
$dataTable = $archive->getDataTable($recordNameFinal);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE));
+
+ $dataTable->filter('Sort', array(Piwik_Metrics::INDEX_ECOMMERCE_ITEM_REVENUE));
$dataTable->queueFilter('ReplaceColumnNames');
$dataTable->queueFilter('ReplaceSummaryRowLabel');
$ordersColumn = 'orders';
if ($abandonedCarts) {
$ordersColumn = 'abandoned_carts';
- $dataTable->renameColumn(Piwik_Archive::INDEX_ECOMMERCE_ORDERS, $ordersColumn);
+ $dataTable->renameColumn(Piwik_Metrics::INDEX_ECOMMERCE_ORDERS, $ordersColumn);
}
// Average price = sum product revenue / quantity
@@ -285,7 +286,7 @@ class Piwik_Goals_API
}
return;
}
- $rowNotDefined = $dataTable->getRowFromLabel(Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED);
+ $rowNotDefined = $dataTable->getRowFromLabel(Piwik_CustomVariables_Archiver::LABEL_CUSTOM_VALUE_NOT_DEFINED);
if ($rowNotDefined) {
$rowNotDefined->setColumn('label', $notDefinedStringPretty);
}
@@ -303,15 +304,15 @@ class Piwik_Goals_API
// If there is not already a 'sum price' for this product
$rowFound = $dataTable->getRowFromLabel($rowView->getColumn('label'));
$price = $rowFound
- ? $rowFound->getColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE)
+ ? $rowFound->getColumn(Piwik_Metrics::INDEX_ECOMMERCE_ITEM_PRICE)
: false;
if (empty($price)) {
// If a price was tracked on the product page
- if ($rowView->getColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED)) {
- $rowView->renameColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED, 'avg_price');
+ if ($rowView->getColumn(Piwik_Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED)) {
+ $rowView->renameColumn(Piwik_Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED, 'avg_price');
}
}
- $rowView->deleteColumn(Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED);
+ $rowView->deleteColumn(Piwik_Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED);
}
$dataTable->addDataTable($ecommerceViews);
@@ -337,17 +338,17 @@ class Piwik_Goals_API
* their integer equivalents.
*
* Checks for the following values:
- * Piwik_Archive::LABEL_ECOMMERCE_ORDER
- * Piwik_Archive::LABEL_ECOMMERCE_CART
+ * Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER
+ * Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART
*
* @param string|int $idGoal The goal id as an integer or a special string.
* @return int The numeric goal id.
*/
protected static function convertSpecialGoalIds($idGoal)
{
- if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
return Piwik_Tracker_GoalManager::IDGOAL_ORDER;
- } else if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_CART) {
+ } else if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
return Piwik_Tracker_GoalManager::IDGOAL_CART;
} else {
return $idGoal;
@@ -376,12 +377,12 @@ class Piwik_Goals_API
if (empty($columns)) {
$columns = Piwik_Goals::getGoalColumns($idGoal);
- if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
$columns[] = 'avg_order_revenue';
}
}
if (in_array('avg_order_revenue', $columns)
- && $idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER
+ && $idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER
) {
$columns[] = 'nb_conversions';
$columns[] = 'revenue';
@@ -389,7 +390,7 @@ class Piwik_Goals_API
}
$columnsToSelect = array();
foreach ($columns as &$columnName) {
- $columnsToSelect[] = Piwik_Goals::getRecordName($columnName, $idGoal);
+ $columnsToSelect[] = Piwik_Goals_Archiver::getRecordName($columnName, $idGoal);
}
$dataTable = $archive->getDataTableFromNumeric($columnsToSelect);
@@ -397,7 +398,7 @@ class Piwik_Goals_API
foreach ($columnsToSelect as $id => $oldName) {
$dataTable->renameColumn($oldName, $columns[$id]);
}
- if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
if ($dataTable instanceof Piwik_DataTable_Array) {
foreach ($dataTable->getArray() as $row) {
$this->enrichTable($row);
@@ -428,7 +429,7 @@ class Piwik_Goals_API
{
Piwik::checkUserHasViewAccess($idSite);
$archive = Piwik_Archive::build($idSite, $period, $date, $segment);
- $dataTable = $archive->getNumeric($toFetch);
+ $dataTable = $archive->getDataTableFromNumeric($toFetch);
return $dataTable;
}
@@ -437,7 +438,7 @@ class Piwik_Goals_API
*/
public function getConversions($idSite, $period, $date, $segment = false, $idGoal = false)
{
- return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals::getRecordName('nb_conversions', $idGoal));
+ return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals_Archiver::getRecordName('nb_conversions', $idGoal));
}
/**
@@ -445,7 +446,7 @@ class Piwik_Goals_API
*/
public function getNbVisitsConverted($idSite, $period, $date, $segment = false, $idGoal = false)
{
- return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals::getRecordName('nb_visits_converted', $idGoal));
+ return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals_Archiver::getRecordName('nb_visits_converted', $idGoal));
}
/**
@@ -453,7 +454,7 @@ class Piwik_Goals_API
*/
public function getConversionRate($idSite, $period, $date, $segment = false, $idGoal = false)
{
- return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals::getRecordName('conversion_rate', $idGoal));
+ return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals_Archiver::getRecordName('conversion_rate', $idGoal));
}
/**
@@ -461,7 +462,7 @@ class Piwik_Goals_API
*/
public function getRevenue($idSite, $period, $date, $segment = false, $idGoal = false)
{
- return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals::getRecordName('revenue', $idGoal));
+ return $this->getNumeric($idSite, $period, $date, $segment, Piwik_Goals_Archiver::getRecordName('revenue', $idGoal));
}
/**
@@ -487,7 +488,7 @@ class Piwik_Goals_API
$realGoalId = $idGoal != true ? false : self::convertSpecialGoalIds($idGoal);
// get the data table
- $dataTable = $archive->getDataTable(Piwik_Goals::getRecordName($recordName, $realGoalId), $idSubtable = null);
+ $dataTable = $archive->getDataTable(Piwik_Goals_Archiver::getRecordName($recordName, $realGoalId), $idSubtable = null);
$dataTable->queueFilter('ReplaceColumnNames');
return $dataTable;
@@ -507,7 +508,7 @@ class Piwik_Goals_API
public function getDaysToConversion($idSite, $period, $date, $segment = false, $idGoal = false)
{
$dataTable = $this->getGoalSpecificDataTable(
- Piwik_Goals::DAYS_UNTIL_CONV_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
+ Piwik_Goals_Archiver::DAYS_UNTIL_CONV_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
$dataTable->queueFilter('Sort', array('label', 'asc', true));
$dataTable->queueFilter(
@@ -530,7 +531,7 @@ class Piwik_Goals_API
public function getVisitsUntilConversion($idSite, $period, $date, $segment = false, $idGoal = false)
{
$dataTable = $this->getGoalSpecificDataTable(
- Piwik_Goals::VISITS_UNTIL_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
+ Piwik_Goals_Archiver::VISITS_UNTIL_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
$dataTable->queueFilter('Sort', array('label', 'asc', true));
$dataTable->queueFilter(
diff --git a/plugins/Goals/Archiver.php b/plugins/Goals/Archiver.php
new file mode 100644
index 0000000000..425a7050b9
--- /dev/null
+++ b/plugins/Goals/Archiver.php
@@ -0,0 +1,424 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_Goals
+ */
+
+class Piwik_Goals_Archiver extends Piwik_PluginsArchiver
+{
+ const VISITS_UNTIL_RECORD_NAME = 'visits_until_conv';
+ const DAYS_UNTIL_CONV_RECORD_NAME = 'days_until_conv';
+ const ITEMS_SKU_RECORD_NAME = 'Goals_ItemsSku';
+ const ITEMS_NAME_RECORD_NAME = 'Goals_ItemsName';
+ const ITEMS_CATEGORY_RECORD_NAME = 'Goals_ItemsCategory';
+ const SKU_FIELD = 'idaction_sku';
+ const NAME_FIELD = 'idaction_name';
+ const CATEGORY_FIELD = 'idaction_category';
+ const CATEGORY2_FIELD = 'idaction_category2';
+ const CATEGORY3_FIELD = 'idaction_category3';
+ const CATEGORY4_FIELD = 'idaction_category4';
+ const CATEGORY5_FIELD = 'idaction_category5';
+ const NO_LABEL = ':';
+ const LOG_CONVERSION_TABLE = 'log_conversion';
+ const VISITS_COUNT_FIELD = 'visitor_count_visits';
+ const DAYS_SINCE_FIRST_VISIT_FIELD = 'visitor_days_since_first';
+ /**
+ * This array stores the ranges to use when displaying the 'visits to conversion' report
+ */
+ public static $visitCountRanges = array(
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 6),
+ array(7, 7),
+ array(8, 8),
+ array(9, 14),
+ array(15, 25),
+ array(26, 50),
+ array(51, 100),
+ array(100)
+ );
+ /**
+ * This array stores the ranges to use when displaying the 'days to conversion' report
+ */
+ public static $daysToConvRanges = array(
+ array(0, 0),
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 6),
+ array(7, 7),
+ array(8, 14),
+ array(15, 30),
+ array(31, 60),
+ array(61, 120),
+ array(121, 364),
+ array(364)
+ );
+ protected $dimensionRecord = array(
+ self::SKU_FIELD => self::ITEMS_SKU_RECORD_NAME,
+ self::NAME_FIELD => self::ITEMS_NAME_RECORD_NAME,
+ self::CATEGORY_FIELD => self::ITEMS_CATEGORY_RECORD_NAME
+ );
+
+ /**
+ * Array containing one DataArray for each Ecommerce items dimension (name/sku/category abandoned carts and orders)
+ * @var array
+ */
+ protected $itemReports = array();
+
+ public function archiveDay()
+ {
+ $this->archiveGeneralGoalMetrics();
+ $this->archiveEcommerceItems();
+ }
+
+ protected function archiveGeneralGoalMetrics()
+ {
+ $prefixes = array(
+ self::VISITS_UNTIL_RECORD_NAME => 'vcv',
+ self::DAYS_UNTIL_CONV_RECORD_NAME => 'vdsf',
+ );
+ $aggregatesMetadata = array(
+ array(self::VISITS_COUNT_FIELD, self::$visitCountRanges, self::LOG_CONVERSION_TABLE, $prefixes[self::VISITS_UNTIL_RECORD_NAME]),
+ array(self::DAYS_SINCE_FIRST_VISIT_FIELD, self::$daysToConvRanges, self::LOG_CONVERSION_TABLE, $prefixes[self::DAYS_UNTIL_CONV_RECORD_NAME]),
+ );
+ $selects = array();
+ foreach ($aggregatesMetadata as $aggregateMetadata) {
+ $selects = array_merge($selects, Piwik_DataAccess_LogAggregator::getSelectsFromRangedColumn($aggregateMetadata));
+ }
+
+ $query = $this->getLogAggregator()->queryConversionsByDimension(array(), false, $selects);
+ if ($query === false) {
+ return;
+ }
+
+ $totalConversions = $totalRevenue = 0;
+ $goals = new Piwik_DataArray();
+ $visitsToConversions = $daysToConversions = array();
+
+ $conversionMetrics = $this->getLogAggregator()->getConversionsMetricFields();
+ while ($row = $query->fetch()) {
+ $idGoal = $row['idgoal'];
+ unset($row['idgoal']);
+ unset($row['label']);
+
+ $values = array();
+ foreach($conversionMetrics as $field => $statement) {
+ $values[$field] = $row[$field];
+ }
+ $goals->sumMetrics($idGoal, $values);
+
+ if (empty($visitsToConversions[$idGoal])) {
+ $visitsToConversions[$idGoal] = new Piwik_DataTable();
+ }
+ $array = Piwik_DataAccess_LogAggregator::makeArrayOneColumn($row, Piwik_Metrics::INDEX_NB_CONVERSIONS, $prefixes[self::VISITS_UNTIL_RECORD_NAME]);
+ $visitsToConversions[$idGoal]->addDataTable(Piwik_DataTable::makeFromIndexedArray($array));
+
+ if (empty($daysToConversions[$idGoal])) {
+ $daysToConversions[$idGoal] = new Piwik_DataTable();
+ }
+ $array = Piwik_DataAccess_LogAggregator::makeArrayOneColumn($row, Piwik_Metrics::INDEX_NB_CONVERSIONS, $prefixes[self::DAYS_UNTIL_CONV_RECORD_NAME]);
+ $daysToConversions[$idGoal]->addDataTable(Piwik_DataTable::makeFromIndexedArray($array));
+
+ // We don't want to sum Abandoned cart metrics in the overall revenue/conversions/converted visits
+ // since it is a "negative conversion"
+ if ($idGoal != Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ $totalConversions += $row[Piwik_Metrics::INDEX_GOAL_NB_CONVERSIONS];
+ $totalRevenue += $row[Piwik_Metrics::INDEX_GOAL_REVENUE];
+ }
+ }
+
+ // Stats by goal, for all visitors
+ $numericRecords = $this->getConversionsNumericMetrics($goals);
+ $this->getProcessor()->insertNumericRecords($numericRecords);
+
+ $this->insertReports(self::VISITS_UNTIL_RECORD_NAME, $visitsToConversions);
+ $this->insertReports(self::DAYS_UNTIL_CONV_RECORD_NAME, $daysToConversions);
+
+ // Stats for all goals
+ $nbConvertedVisits = $this->getProcessor()->getNumberOfVisitsConverted();
+ $metrics = array(
+ self::getRecordName('conversion_rate') => $this->getConversionRate($nbConvertedVisits),
+ self::getRecordName('nb_conversions') => $totalConversions,
+ self::getRecordName('nb_visits_converted') => $nbConvertedVisits,
+ self::getRecordName('revenue') => $totalRevenue,
+ );
+ $this->getProcessor()->insertNumericRecords($metrics);
+ }
+
+ protected function getConversionsNumericMetrics(Piwik_DataArray $goals)
+ {
+ $numericRecords = array();
+ $goals = $goals->getDataArray();
+ foreach ($goals as $idGoal => $array) {
+ foreach ($array as $metricId => $value) {
+ $metricName = Piwik_Metrics::$mappingFromIdToNameGoal[$metricId];
+ $recordName = self::getRecordName($metricName, $idGoal);
+ $numericRecords[$recordName] = $value;
+ }
+ if(!empty($array[Piwik_Metrics::INDEX_GOAL_NB_VISITS_CONVERTED])) {
+ $conversion_rate = $this->getConversionRate($array[Piwik_Metrics::INDEX_GOAL_NB_VISITS_CONVERTED]);
+ $recordName = self::getRecordName('conversion_rate', $idGoal);
+ $numericRecords[$recordName] = $conversion_rate;
+ }
+ }
+ return $numericRecords;
+ }
+
+ /**
+ * @param string $recordName 'nb_conversions'
+ * @param int|bool $idGoal idGoal to return the metrics for, or false to return overall
+ * @return string Archive record name
+ */
+ static public function getRecordName($recordName, $idGoal = false)
+ {
+ $idGoalStr = '';
+ if ($idGoal !== false) {
+ $idGoalStr = $idGoal . "_";
+ }
+ return 'Goal_' . $idGoalStr . $recordName;
+ }
+
+ protected function getConversionRate($count)
+ {
+ $visits = $this->getProcessor()->getNumberOfVisits();
+ return round(100 * $count / $visits, Piwik_Tracker_GoalManager::REVENUE_PRECISION);
+ }
+
+ protected function insertReports($recordName, $visitsToConversions)
+ {
+ foreach ($visitsToConversions as $idGoal => $table) {
+ $record = self::getRecordName($recordName, $idGoal);
+ $this->getProcessor()->insertBlobRecord($record, $table->getSerialized());
+ }
+ $overviewTable = $this->getOverviewFromGoalTables($visitsToConversions);
+ $this->getProcessor()->insertBlobRecord(self::getRecordName($recordName), $overviewTable->getSerialized());
+ }
+
+ protected function getOverviewFromGoalTables($tableByGoal)
+ {
+ $overview = new Piwik_DataTable();
+ foreach ($tableByGoal as $idGoal => $table) {
+ if ($this->isStandardGoal($idGoal)) {
+ $overview->addDataTable($table);
+ }
+ }
+ return $overview;
+ }
+
+ protected function isStandardGoal($idGoal)
+ {
+ return !in_array($idGoal, $this->getEcommerceIdGoals());
+ }
+
+ protected function archiveEcommerceItems()
+ {
+ if (!$this->shouldArchiveEcommerceItems()) {
+ return false;
+ }
+ $this->initItemReports();
+ foreach ($this->getItemsDimensions() as $dimension) {
+ $query = $this->getLogAggregator()->queryEcommerceItems($dimension);
+ if ($query == false) {
+ continue;
+ }
+ $this->aggregateFromEcommerceItems($query, $dimension);
+ }
+ $this->recordItemReports();
+ }
+
+ protected function initItemReports()
+ {
+ foreach ($this->getEcommerceIdGoals() as $ecommerceType) {
+ foreach ($this->dimensionRecord as $dimension => $record) {
+ $this->itemReports[$dimension][$ecommerceType] = new Piwik_DataArray();
+ }
+ }
+ }
+
+ protected function recordItemReports()
+ {
+ /** @var Piwik_DataArray $array */
+ foreach ($this->itemReports as $dimension => $itemAggregatesByType) {
+ foreach ($itemAggregatesByType as $ecommerceType => $itemAggregate) {
+ $recordName = $this->dimensionRecord[$dimension];
+ if ($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ $recordName = self::getItemRecordNameAbandonedCart($recordName);
+ }
+ $table = $this->getProcessor()->getDataTableFromDataArray($itemAggregate);
+ $this->getProcessor()->insertBlobRecord($recordName, $table->getSerialized());
+ }
+ }
+ }
+
+ protected function shouldArchiveEcommerceItems()
+ {
+ // Per item doesn't support segment
+ // Also, when querying Goal metrics for visitorType==returning, we wouldnt want to trigger an extra request
+ // event if it did support segment
+ if (!$this->getProcessor()->getSegment()->isEmpty()) {
+ return false;
+ }
+ return true;
+ }
+
+ protected function getItemsDimensions()
+ {
+ $dimensions = array_keys($this->dimensionRecord);
+ foreach ($this->getItemExtraCategories() as $category) {
+ $dimensions[] = $category;
+ }
+ return $dimensions;
+ }
+
+ protected function getItemExtraCategories()
+ {
+ return array(self::CATEGORY2_FIELD, self::CATEGORY3_FIELD, self::CATEGORY4_FIELD, self::CATEGORY5_FIELD);
+ }
+
+ protected function isItemExtraCategory($field)
+ {
+ return in_array($field, $this->getItemExtraCategories());
+ }
+
+ protected function aggregateFromEcommerceItems($query, $dimension)
+ {
+ while ($row = $query->fetch()) {
+ $ecommerceType = $row['ecommerceType'];
+
+ $label = $this->cleanupRowGetLabel($row, $dimension);
+ if ($label === false) {
+ continue;
+ }
+
+ // Aggregate extra categories in the Item categories array
+ if ($this->isItemExtraCategory($dimension)) {
+ $array = $this->itemReports[self::CATEGORY_FIELD][$ecommerceType];
+ } else {
+ $array = $this->itemReports[$dimension][$ecommerceType];
+ }
+
+ $this->roundColumnValues($row);
+ $array->sumMetrics($label, $row);
+ }
+ }
+
+ protected function cleanupRowGetLabel(&$row, $currentField)
+ {
+ $label = $row['label'];
+ if (empty($label)) {
+ // An empty additional category -> skip this iteration
+ if ($this->isItemExtraCategory($currentField)) {
+ return false;
+ }
+ $label = "Value not defined";
+ // Product Name/Category not defined"
+ if (class_exists('Piwik_CustomVariables')) {
+ $label = Piwik_CustomVariables_Archiver::LABEL_CUSTOM_VALUE_NOT_DEFINED;
+ }
+ }
+
+ if ($row['ecommerceType'] == Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ // abandoned carts are the numner of visits with an abandoned cart
+ $row[Piwik_Metrics::INDEX_ECOMMERCE_ORDERS] = $row[Piwik_Metrics::INDEX_NB_VISITS];
+ }
+
+ unset($row[Piwik_Metrics::INDEX_NB_VISITS]);
+ unset($row['label']);
+ unset($row['ecommerceType']);
+
+ return $label;
+ }
+
+ protected function roundColumnValues(&$row)
+ {
+ $columnsToRound = array(
+ Piwik_Metrics::INDEX_ECOMMERCE_ITEM_REVENUE,
+ Piwik_Metrics::INDEX_ECOMMERCE_ITEM_QUANTITY,
+ Piwik_Metrics::INDEX_ECOMMERCE_ITEM_PRICE,
+ Piwik_Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED,
+ );
+ foreach ($columnsToRound as $column) {
+ if (isset($row[$column])
+ && $row[$column] == round($row[$column])
+ ) {
+ $row[$column] = round($row[$column]);
+ }
+ }
+ }
+
+ protected function getEcommerceIdGoals()
+ {
+ return array(Piwik_Tracker_GoalManager::IDGOAL_CART, Piwik_Tracker_GoalManager::IDGOAL_ORDER);
+ }
+
+ static public function getItemRecordNameAbandonedCart($recordName)
+ {
+ return $recordName . '_Cart';
+ }
+
+ /**
+ * @param $this->getProcessor()
+ */
+ public function archivePeriod()
+ {
+ /*
+ * Archive Ecommerce Items
+ */
+ if ($this->shouldArchiveEcommerceItems()) {
+ $dataTableToSum = $this->dimensionRecord;
+ foreach ($this->dimensionRecord as $recordName) {
+ $dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
+ }
+ $this->getProcessor()->aggregateDataTableReports($dataTableToSum);
+ }
+
+ /*
+ * Archive General Goal metrics
+ */
+ $goalIdsToSum = Piwik_Tracker_GoalManager::getGoalIds($this->getProcessor()->getSite()->getId());
+
+ //Ecommerce
+ $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_ORDER;
+ $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_CART; //bug here if idgoal=1
+ // Overall goal metrics
+ $goalIdsToSum[] = false;
+
+ $fieldsToSum = array();
+ foreach ($goalIdsToSum as $goalId) {
+ $metricsToSum = Piwik_Goals::getGoalColumns($goalId);
+ unset($metricsToSum[array_search('conversion_rate', $metricsToSum)]);
+ foreach ($metricsToSum as $metricName) {
+ $fieldsToSum[] = self::getRecordName($metricName, $goalId);
+ }
+ }
+ $records = $this->getProcessor()->aggregateNumericMetrics($fieldsToSum);
+
+ // also recording conversion_rate for each goal
+ foreach ($goalIdsToSum as $goalId) {
+ $nb_conversions = $records[self::getRecordName('nb_visits_converted', $goalId)];
+ $conversion_rate = $this->getConversionRate($nb_conversions);
+ $this->getProcessor()->insertNumericRecord(self::getRecordName('conversion_rate', $goalId), $conversion_rate);
+
+ // sum up the visits to conversion data table & the days to conversion data table
+ $this->getProcessor()->aggregateDataTableReports(array(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId),
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)));
+ }
+
+ // sum up goal overview reports
+ $this->getProcessor()->aggregateDataTableReports(array(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME),
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)));
+ }
+} \ No newline at end of file
diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php
index 47752a7a4d..62e76be000 100644
--- a/plugins/Goals/Controller.php
+++ b/plugins/Goals/Controller.php
@@ -33,6 +33,14 @@ class Piwik_Goals_Controller extends Piwik_Controller
private function formatConversionRate($conversionRate)
{
+ if ($conversionRate instanceof Piwik_DataTable) {
+ if ($conversionRate->getRowsCount() == 0) {
+ $conversionRate = 0;
+ } else {
+ $columns = $conversionRate->getFirstRow()->getColumns();
+ $conversionRate = (float)reset($columns);
+ }
+ }
return sprintf('%.' . self::CONVERSION_RATE_PRECISION . 'f%%', $conversionRate);
}
@@ -69,7 +77,7 @@ class Piwik_Goals_Controller extends Piwik_Controller
throw new Exception("Ecommerce Tracking requires that the plugin Custom Variables is enabled. Please enable the plugin CustomVariables (or ask your admin).");
}
- $view = $this->getGoalReportView($idGoal = Piwik_Archive::LABEL_ECOMMERCE_ORDER);
+ $view = $this->getGoalReportView($idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER);
$view->displayFullReport = true;
echo $view->render();
}
@@ -157,7 +165,7 @@ class Piwik_Goals_Controller extends Piwik_Controller
protected function getGoalReportView($idGoal = false)
{
$view = new Piwik_View('@Goals/single_goal');
- if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
$goalDefinition['name'] = Piwik_Translate('Goals_Ecommerce');
$goalDefinition['allow_multiple'] = true;
$ecommerce = $view->ecommerce = true;
@@ -172,8 +180,8 @@ class Piwik_Goals_Controller extends Piwik_Controller
foreach ($goal as $name => $value) {
$view->$name = $value;
}
- if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
- $goal = $this->getMetricsForGoal(Piwik_Archive::LABEL_ECOMMERCE_CART);
+ 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;
@@ -201,7 +209,18 @@ class Piwik_Goals_Controller extends Piwik_Controller
public function index()
{
$view = $this->getOverviewView();
- $view->goalsJSON = Piwik_Common::json_encode($this->goals);
+
+ // 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;
+ foreach ($goals as &$goal) {
+ $goal['name'] = Piwik_Common::unsanitizeInputValue($goal['name']);
+ if (isset($goal['pattern'])) {
+ $goal['pattern'] = Piwik_Common::unsanitizeInputValue($goal['pattern']);
+ }
+ }
+ $view->goalsJSON = Piwik_Common::json_encode($goals);
+
$view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
$view->ecommerceEnabled = $this->site->isEcommerceEnabled();
$view->displayFullReport = true;
@@ -294,9 +313,9 @@ class Piwik_Goals_Controller extends Piwik_Controller
$view->setParametersToModify(array('idGoal' => $idGoal));
$nameToLabel = $this->goalColumnNameToLabel;
- if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
$nameToLabel['nb_conversions'] = 'General_EcommerceOrders';
- } elseif ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_CART) {
+ } elseif ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
$nameToLabel['nb_conversions'] = Piwik_Translate('General_VisitsWith', Piwik_Translate('Goals_AbandonedCart'));
$nameToLabel['conversion_rate'] = $nameToLabel['nb_conversions'];
$nameToLabel['revenue'] = Piwik_Translate('Goals_LeftInCart', Piwik_Translate('Goals_ColumnRevenue'));
@@ -350,7 +369,7 @@ class Piwik_Goals_Controller extends Piwik_Controller
$keywordNotDefinedString = '';
if (Piwik_PluginsManager::getInstance()->isPluginActivated('Referers')) {
- $keywordNotDefinedString = Piwik_Referers::getKeywordNotDefinedString();
+ $keywordNotDefinedString = Piwik_Referers_API::getKeywordNotDefinedString();
$topDimensionsToLoad += array(
'keyword' => 'Referers.getKeywords',
'website' => 'Referers.getWebsites',
@@ -414,7 +433,7 @@ class Piwik_Goals_Controller extends Piwik_Controller
'urlSparklineConversionRate' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => $idGoal)),
'urlSparklineRevenue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => $idGoal)),
);
- if ($idGoal == Piwik_Archive::LABEL_ECOMMERCE_ORDER) {
+ if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
$items = $dataRow->getColumn('items');
$aov = $dataRow->getColumn('avg_order_revenue');
$return = array_merge($return, array(
@@ -447,7 +466,7 @@ class Piwik_Goals_Controller extends Piwik_Controller
$view->setSortedColumn('label', 'asc');
$view->setColumnTranslation('label', Piwik_Translate('Goals_VisitsUntilConv'));
$view->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
- $view->setLimit(count(Piwik_Goals::$visitCountRanges));
+ $view->setLimit(count(Piwik_Goals_Archiver::$visitCountRanges));
$view->disableOffsetInformationAndPaginationControls();
$view->disableShowAllViewsIcons();
return $this->renderView($view, $fetch);
@@ -469,7 +488,7 @@ class Piwik_Goals_Controller extends Piwik_Controller
$view->setColumnTranslation('label', Piwik_Translate('Goals_DaysToConv'));
$view->setColumnTranslation('nb_conversions', Piwik_Translate('Goals_ColumnConversions'));
$view->disableShowAllViewsIcons();
- $view->setLimit(count(Piwik_Goals::$daysToConvRanges));
+ $view->setLimit(count(Piwik_Goals_Archiver::$daysToConvRanges));
$view->disableOffsetInformationAndPaginationControls();
return $this->renderView($view, $fetch);
}
@@ -542,7 +561,7 @@ class Piwik_ViewDataTable_HtmlTable_EcommerceOrder extends Piwik_ViewDataTable_H
{
protected function getViewDataTableId()
{
- return Piwik_Archive::LABEL_ECOMMERCE_ORDER;
+ return Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER;
}
}
@@ -550,6 +569,6 @@ class Piwik_ViewDataTable_HtmlTable_EcommerceAbandonedCart extends Piwik_ViewDat
{
protected function getViewDataTableId()
{
- return Piwik_Archive::LABEL_ECOMMERCE_CART;
+ return Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART;
}
}
diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php
index dd8a138bc4..2ae78f60cc 100644
--- a/plugins/Goals/Goals.php
+++ b/plugins/Goals/Goals.php
@@ -15,49 +15,51 @@
*/
class Piwik_Goals extends Piwik_Plugin
{
- const VISITS_UNTIL_RECORD_NAME = 'visits_until_conv';
- const DAYS_UNTIL_CONV_RECORD_NAME = 'days_until_conv';
-
- /**
- * This array stores the ranges to use when displaying the 'visits to conversion'
- * report.
- */
- public static $visitCountRanges = array(
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 6),
- array(7, 7),
- array(8, 8),
- array(9, 14),
- array(15, 25),
- array(26, 50),
- array(51, 100),
- array(100)
+ protected $ecommerceReports = array(
+ array('Goals_ProductSKU', 'Goals', 'getItemsSku'),
+ array('Goals_ProductName', 'Goals', 'getItemsName'),
+ array('Goals_ProductCategory', 'Goals', 'getItemsCategory')
);
- /**
- * This array stores the ranges to use when displaying the 'days to conversion'
- * report.
- */
- public static $daysToConvRanges = array(
- array(0, 0),
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 6),
- array(7, 7),
- array(8, 14),
- array(15, 30),
- array(31, 60),
- array(61, 120),
- array(121, 364),
- array(364)
- );
+ static public function getReportsWithGoalMetrics()
+ {
+ $dimensions = array();
+ Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $dimensions);
+ $dimensionsByGroup = array();
+ foreach ($dimensions as $dimension) {
+ $group = $dimension['category'];
+ unset($dimension['category']);
+ $dimensionsByGroup[$group][] = $dimension;
+ }
+ return $dimensionsByGroup;
+ }
+
+ static public function getGoalColumns($idGoal)
+ {
+ $columns = array(
+ 'nb_conversions',
+ 'nb_visits_converted',
+ 'conversion_rate',
+ 'revenue',
+ );
+ if ($idGoal === false) {
+ return $columns;
+ }
+ // Orders
+ if ($idGoal === Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ $columns = array_merge($columns, array(
+ 'revenue_subtotal',
+ 'revenue_tax',
+ 'revenue_shipping',
+ 'revenue_discount',
+ ));
+ }
+ // Abandoned carts & orders
+ if ($idGoal <= Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ $columns[] = 'items';
+ }
+ return $columns;
+ }
public function getInformation()
{
@@ -237,7 +239,7 @@ class Piwik_Goals extends Piwik_Plugin
'name' => Piwik_Translate('General_EcommerceOrders'),
'module' => 'Goals',
'action' => 'get',
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER),
+ 'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER),
'metrics' => $ecommerceMetrics,
'processedMetrics' => false,
'order' => 10
@@ -250,7 +252,7 @@ class Piwik_Goals extends Piwik_Plugin
'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
'constantRowsCount' => true,
'metrics' => $conversionReportMetrics,
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER),
+ 'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER),
'order' => 11
);
$reports[] = array(
@@ -261,7 +263,7 @@ class Piwik_Goals extends Piwik_Plugin
'dimension' => Piwik_Translate('Goals_DaysToConv'),
'constantRowsCount' => true,
'metrics' => $conversionReportMetrics,
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER),
+ 'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER),
'order' => 12
);
@@ -278,7 +280,7 @@ class Piwik_Goals extends Piwik_Plugin
'name' => Piwik_Translate('General_AbandonedCarts'),
'module' => 'Goals',
'action' => 'get',
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART),
+ 'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART),
'metrics' => $abandonedCartMetrics,
'processedMetrics' => false,
'order' => 15
@@ -292,7 +294,7 @@ class Piwik_Goals extends Piwik_Plugin
'dimension' => Piwik_Translate('Goals_VisitsUntilConv'),
'constantRowsCount' => true,
'metrics' => $conversionReportMetrics,
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART),
+ 'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART),
'order' => 20
);
$reports[] = array(
@@ -303,7 +305,7 @@ class Piwik_Goals extends Piwik_Plugin
'dimension' => Piwik_Translate('Goals_DaysToConv'),
'constantRowsCount' => true,
'metrics' => $conversionReportMetrics,
- 'parameters' => array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_CART),
+ 'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART),
'order' => 25
);
@@ -348,28 +350,6 @@ class Piwik_Goals extends Piwik_Plugin
}
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => 'General_VisitConvertedGoalId',
- 'segment' => 'visitConvertedGoalId',
- 'sqlSegment' => 'log_conversion.idgoal',
- 'acceptedValues' => '1, 2, 3, etc.',
- );
- }
-
- protected $ecommerceReports = array(
- array('Goals_ProductSKU', 'Goals', 'getItemsSku'),
- array('Goals_ProductName', 'Goals', 'getItemsName'),
- array('Goals_ProductCategory', 'Goals', 'getItemsCategory')
- );
-
static public function getProductReportColumns()
{
return array(
@@ -383,17 +363,44 @@ class Piwik_Goals extends Piwik_Plugin
);
}
- static public function getReportsWithGoalMetrics()
+ /**
+ * This function executes when the 'Goals.getReportsWithGoalMetrics' event fires. It
+ * adds the 'visits to conversion' report metadata to the list of goal reports so
+ * this report will be displayed.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getActualReportsWithGoalMetrics($notification)
{
- $dimensions = array();
- Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $dimensions);
- $dimensionsByGroup = array();
- foreach ($dimensions as $dimension) {
- $group = $dimension['category'];
- unset($dimension['category']);
- $dimensionsByGroup[$group][] = $dimension;
- }
- return $dimensionsByGroup;
+ $dimensions =& $notification->getNotificationObject();
+ $dimensions = array_merge($dimensions, array(
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('Goals_VisitsUntilConv'),
+ 'module' => 'Goals',
+ 'action' => 'getVisitsUntilConversion'
+ ),
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('Goals_DaysToConv'),
+ 'module' => 'Goals',
+ 'action' => 'getDaysToConversion'
+ )
+ ));
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => Piwik_Translate('General_Visit'),
+ 'name' => 'General_VisitConvertedGoalId',
+ 'segment' => 'visitConvertedGoalId',
+ 'sqlSegment' => 'log_conversion.idgoal',
+ 'acceptedValues' => '1, 2, 3, etc.',
+ );
}
/**
@@ -433,7 +440,7 @@ class Piwik_Goals extends Piwik_Plugin
// Ecommerce widgets
$site = new Piwik_Site($idSite);
if ($site->isEcommerceEnabled()) {
- Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceOverview', 'Goals', 'widgetGoalReport', array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER));
+ Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceOverview', 'Goals', 'widgetGoalReport', array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER));
Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceLog', 'Goals', 'getEcommerceLog');
foreach ($this->ecommerceReports as $widget) {
Piwik_AddWidget('Goals_Ecommerce', $widget[0], $widget[1], $widget[2]);
@@ -450,13 +457,6 @@ class Piwik_Goals extends Piwik_Plugin
}
}
- protected function getGoalCategoryName($idSite)
- {
- $site = new Piwik_Site($idSite);
- return $site->isEcommerceEnabled() ? 'Goals_EcommerceAndGoalsMenu' : 'Goals_Goals';
- }
-
-
function addMenus()
{
$idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
@@ -467,23 +467,23 @@ class Piwik_Goals extends Piwik_Plugin
Piwik_AddMenu($mainGoalMenu, '', array(
'module' => 'Goals',
'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'addNewGoal'),
- 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
+ 'idGoal' => ($site->isEcommerceEnabled() ? Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER : null)),
true,
25);
if ($site->isEcommerceEnabled()) {
- Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
+ Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER), true, 1);
}
Piwik_AddMenu($mainGoalMenu, 'Goals_AddNewGoal', array('module' => 'Goals', 'action' => 'addNewGoal'));
} else {
Piwik_AddMenu($mainGoalMenu, '', array(
'module' => 'Goals',
'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'index'),
- 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
+ 'idGoal' => ($site->isEcommerceEnabled() ? Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER : null)),
true,
25);
if ($site->isEcommerceEnabled()) {
- Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
+ Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER), true, 1);
}
Piwik_AddMenu($mainGoalMenu, 'Goals_GoalsOverview', array('module' => 'Goals', 'action' => 'index'), true, 2);
foreach ($goals as $goal) {
@@ -492,111 +492,10 @@ class Piwik_Goals extends Piwik_Plugin
}
}
- /**
- * @param string $recordName 'nb_conversions'
- * @param int|bool $idGoal idGoal to return the metrics for, or false to return overall
- * @return string Archive record name
- */
- static public function getRecordName($recordName, $idGoal = false)
- {
- $idGoalStr = '';
- if ($idGoal !== false) {
- $idGoalStr = $idGoal . "_";
- }
- return 'Goal_' . $idGoalStr . $recordName;
- }
-
- /**
- * Hooks on Period archiving.
- * Sums up Goal conversions stats, and processes overall conversion rate
- *
- * @param Piwik_Event_Notification $notification
- * @return void
- */
- function archivePeriod($notification)
- {
- /**
- * @var Piwik_ArchiveProcessing
- */
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- /*
- * Archive Ecommerce Items
- */
- if ($this->shouldArchiveEcommerceItems($archiveProcessing)) {
- $dataTableToSum = $this->dimensions;
- foreach ($this->dimensions as $recordName) {
- $dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
- }
- $archiveProcessing->archiveDataTable($dataTableToSum);
- }
-
- /*
- * Archive General Goal metrics
- */
- $goalIdsToSum = Piwik_Tracker_GoalManager::getGoalIds($archiveProcessing->idsite);
-
- //Ecommerce
- $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_ORDER;
- $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_CART; //bug here if idgoal=1
- // Overall goal metrics
- $goalIdsToSum[] = false;
-
- $fieldsToSum = array();
- foreach ($goalIdsToSum as $goalId) {
- $metricsToSum = Piwik_Goals::getGoalColumns($goalId);
- unset($metricsToSum[array_search('conversion_rate', $metricsToSum)]);
- foreach ($metricsToSum as $metricName) {
- $fieldsToSum[] = self::getRecordName($metricName, $goalId);
- }
- }
- $records = $archiveProcessing->archiveNumericValuesSum($fieldsToSum);
-
- // also recording conversion_rate for each goal
- foreach ($goalIdsToSum as $goalId) {
- $nb_conversions = $records[self::getRecordName('nb_visits_converted', $goalId)];
- $conversion_rate = $this->getConversionRate($nb_conversions, $archiveProcessing);
- $archiveProcessing->insertNumericRecord(self::getRecordName('conversion_rate', $goalId), $conversion_rate);
-
- // sum up the visits to conversion data table & the days to conversion data table
- $archiveProcessing->archiveDataTable(array(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId),
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)));
- }
-
- // sum up goal overview reports
- $archiveProcessing->archiveDataTable(array(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME),
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)));
- }
-
- static public function getGoalColumns($idGoal)
+ protected function getGoalCategoryName($idSite)
{
- $columns = array(
- 'nb_conversions',
- 'nb_visits_converted',
- 'conversion_rate',
- 'revenue',
- );
- if ($idGoal === false) {
- return $columns;
- }
- // Orders
- if ($idGoal === Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
- $columns = array_merge($columns, array(
- 'revenue_subtotal',
- 'revenue_tax',
- 'revenue_shipping',
- 'revenue_discount',
- ));
- }
- // Abandoned carts & orders
- if ($idGoal <= Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
- $columns[] = 'items';
- }
- return $columns;
+ $site = new Piwik_Site($idSite);
+ return $site->isEcommerceEnabled() ? 'Goals_EcommerceAndGoalsMenu' : 'Goals_Goals';
}
/**
@@ -610,260 +509,30 @@ class Piwik_Goals extends Piwik_Plugin
function archiveDay($notification)
{
/**
- * @var Piwik_ArchiveProcessing_Day
+ * @var Piwik_ArchiveProcessor_Day
*/
- $archiveProcessing = $notification->getNotificationObject();
+ $archiveProcessor = $notification->getNotificationObject();
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveGeneralGoalMetrics($archiveProcessing);
- $this->archiveEcommerceItems($archiveProcessing);
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- function archiveGeneralGoalMetrics($archiveProcessing)
- {
- // extra aggregate selects for the visits to conversion report
- $visitToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_count_visits', self::$visitCountRanges, 'log_conversion', 'vcv');
-
- // extra aggregate selects for the days to conversion report
- $daysToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_days_since_first', self::$daysToConvRanges, 'log_conversion', 'vdsf');
-
- $query = $archiveProcessing->queryConversionsByDimension(
- array(), '', array_merge($visitToConvExtraCols, $daysToConvExtraCols));
-
- if ($query === false) {
- return;
+ $archiving = new Piwik_Goals_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archiveDay();
}
-
- $goals = array();
- $visitsToConvReport = array();
- $daysToConvReport = array();
-
- // Get a standard empty goal row
- $overall = $archiveProcessing->getNewGoalRow($idGoal = 1);
- while ($row = $query->fetch()) {
- $idgoal = $row['idgoal'];
-
- if (!isset($goals[$idgoal])) {
- $goals[$idgoal] = $archiveProcessing->getNewGoalRow($idgoal);
-
- $visitsToConvReport[$idgoal] = new Piwik_DataTable();
- $daysToConvReport[$idgoal] = new Piwik_DataTable();
- }
- $archiveProcessing->updateGoalStats($row, $goals[$idgoal]);
-
- // We don't want to sum Abandoned cart metrics in the overall revenue/conversions/converted visits
- // since it is a "negative conversion"
- if ($idgoal != Piwik_Tracker_GoalManager::IDGOAL_CART) {
- $archiveProcessing->updateGoalStats($row, $overall);
- }
-
- // map the goal + visit number of a visitor with the # of conversions that happened on that visit
- $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vcv');
- $visitsToConvReport[$idgoal]->addDataTable($table);
-
- // map the goal + day number of a visit with the # of conversion that happened on that day
- $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vdsf');
- $daysToConvReport[$idgoal]->addDataTable($table);
- }
-
- // these data tables hold reports for every goal of a site
- $visitsToConvOverview = new Piwik_DataTable();
- $daysToConvOverview = new Piwik_DataTable();
-
- // Stats by goal, for all visitors
- foreach ($goals as $idgoal => $values) {
- foreach ($values as $metricId => $value) {
- $metricName = Piwik_Archive::$mappingFromIdToNameGoal[$metricId];
- $recordName = self::getRecordName($metricName, $idgoal);
- $archiveProcessing->insertNumericRecord($recordName, $value);
- }
- $conversion_rate = $this->getConversionRate($values[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED], $archiveProcessing);
- $recordName = self::getRecordName('conversion_rate', $idgoal);
- $archiveProcessing->insertNumericRecord($recordName, $conversion_rate);
-
- // if the goal is not a special goal (like ecommerce) add it to the overview report
- if ($idgoal !== Piwik_Tracker_GoalManager::IDGOAL_CART &&
- $idgoal !== Piwik_Tracker_GoalManager::IDGOAL_ORDER
- ) {
- $visitsToConvOverview->addDataTable($visitsToConvReport[$idgoal]);
- $daysToConvOverview->addDataTable($daysToConvReport[$idgoal]);
- }
-
- // visit count until conversion stats
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $idgoal),
- $visitsToConvReport[$idgoal]->getSerialized());
-
- // day count until conversion stats
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $idgoal),
- $daysToConvReport[$idgoal]->getSerialized());
- }
-
- // archive overview reports
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME), $visitsToConvOverview->getSerialized());
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME), $daysToConvOverview->getSerialized());
-
- // Stats for all goals
- $totalAllGoals = array(
- self::getRecordName('conversion_rate') => $this->getConversionRate($archiveProcessing->getNumberOfVisitsConverted(), $archiveProcessing),
- self::getRecordName('nb_conversions') => $overall[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS],
- self::getRecordName('nb_visits_converted') => $archiveProcessing->getNumberOfVisitsConverted(),
- self::getRecordName('revenue') => $overall[Piwik_Archive::INDEX_GOAL_REVENUE],
- );
- foreach ($totalAllGoals as $recordName => $value) {
- $archiveProcessing->insertNumericRecord($recordName, $value);
- }
- }
-
- protected $dimensions = array(
- 'idaction_sku' => 'Goals_ItemsSku',
- 'idaction_name' => 'Goals_ItemsName',
- 'idaction_category' => 'Goals_ItemsCategory'
- );
-
- protected function shouldArchiveEcommerceItems($archiveProcessing)
- {
- // Per item doesn't support segment
- // Also, when querying Goal metrics for visitorType==returning, we wouldnt want to trigger an extra request
- // event if it did support segment
- // (if this is implented, we should have shouldProcessReportsForPlugin() support partial archiving based on which metric is requested)
- if (!$archiveProcessing->getSegment()->isEmpty()) {
- return false;
- }
- return true;
}
/**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ * Hooks on Period archiving.
+ * Sums up Goal conversions stats, and processes overall conversion rate
+ *
+ * @param Piwik_Event_Notification $notification
+ * @return void
*/
- function archiveEcommerceItems($archiveProcessing)
+ function archivePeriod($notification)
{
- if (!$this->shouldArchiveEcommerceItems($archiveProcessing)) {
- return false;
- }
- $items = array();
-
- $dimensionsToQuery = $this->dimensions;
- $dimensionsToQuery['idaction_category2'] = 'AdditionalCategory';
- $dimensionsToQuery['idaction_category3'] = 'AdditionalCategory';
- $dimensionsToQuery['idaction_category4'] = 'AdditionalCategory';
- $dimensionsToQuery['idaction_category5'] = 'AdditionalCategory';
-
- foreach ($dimensionsToQuery as $dimension => $recordName) {
- $query = $archiveProcessing->queryEcommerceItems($dimension);
- if ($query == false) {
- continue;
- }
+ $archiveProcessor = $notification->getNotificationObject();
- while ($row = $query->fetch()) {
- $label = $row['label'];
- $ecommerceType = $row['ecommerceType'];
-
- if (empty($label)) {
- // idaction==0 case:
- // If we are querying any optional category, we do not include idaction=0
- // Otherwise we over-report in the Product Categories report
- if ($recordName == 'AdditionalCategory') {
- continue;
- }
- // Product Name/Category not defined"
- if (class_exists('Piwik_CustomVariables')) {
- $label = Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED;
- } else {
- $label = "Value not defined";
- }
- }
- // For carts, idorder = 0. To count abandoned carts, we must count visits with an abandoned cart
- if ($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART) {
- $row[Piwik_Archive::INDEX_ECOMMERCE_ORDERS] = $row[Piwik_Archive::INDEX_NB_VISITS];
- }
- unset($row[Piwik_Archive::INDEX_NB_VISITS]);
- unset($row['label']);
- unset($row['ecommerceType']);
-
- $columnsToRound = array(
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE,
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY,
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE,
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED,
- );
- foreach ($columnsToRound as $column) {
- if (isset($row[$column])
- && $row[$column] == round($row[$column])
- ) {
- $row[$column] = round($row[$column]);
- }
- }
- $items[$dimension][$ecommerceType][$label] = $row;
- }
- }
-
- foreach ($this->dimensions as $dimension => $recordName) {
- foreach (array(Piwik_Tracker_GoalManager::IDGOAL_CART, Piwik_Tracker_GoalManager::IDGOAL_ORDER) as $ecommerceType) {
- if (!isset($items[$dimension][$ecommerceType])) {
- continue;
- }
- $recordNameInsert = $recordName;
- if ($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART) {
- $recordNameInsert = self::getItemRecordNameAbandonedCart($recordName);
- }
- $table = $archiveProcessing->getDataTableFromArray($items[$dimension][$ecommerceType]);
-
- // For "category" report, we aggregate all 5 category queries into one datatable
- if ($dimension == 'idaction_category') {
- foreach (array('idaction_category2', 'idaction_category3', 'idaction_category4', 'idaction_category5') as $categoryToSum) {
- if (!empty($items[$categoryToSum][$ecommerceType])) {
- $tableToSum = $archiveProcessing->getDataTableFromArray($items[$categoryToSum][$ecommerceType]);
- $table->addDataTable($tableToSum);
- }
- }
- }
- $archiveProcessing->insertBlobRecord($recordNameInsert, $table->getSerialized());
- }
+ $archiving = new Piwik_Goals_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
}
}
-
- static public function getItemRecordNameAbandonedCart($recordName)
- {
- return $recordName . '_Cart';
- }
-
- function getConversionRate($count, $archiveProcessing)
- {
- $visits = $archiveProcessing->getNumberOfVisits();
- return round(100 * $count / $visits, Piwik_Tracker_GoalManager::REVENUE_PRECISION);
- }
-
- /**
- * This function executes when the 'Goals.getReportsWithGoalMetrics' event fires. It
- * adds the 'visits to conversion' report metadata to the list of goal reports so
- * this report will be displayed.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getActualReportsWithGoalMetrics($notification)
- {
- $dimensions =& $notification->getNotificationObject();
- $dimensions = array_merge($dimensions, array(
- array('category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('Goals_VisitsUntilConv'),
- 'module' => 'Goals',
- 'action' => 'getVisitsUntilConversion'
- ),
- array('category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('Goals_DaysToConv'),
- 'module' => 'Goals',
- 'action' => 'getDaysToConversion'
- )
- ));
- }
}
diff --git a/plugins/Goals/javascripts/goalsForm.js b/plugins/Goals/javascripts/goalsForm.js
index fa1d0beceb..354e061978 100644
--- a/plugins/Goals/javascripts/goalsForm.js
+++ b/plugins/Goals/javascripts/goalsForm.js
@@ -155,7 +155,7 @@ function bindListGoalEdit() {
var goalId = $(this).attr('id');
var goal = piwik.goals[goalId];
- $('#confirm h2').text(sprintf(_pk_translate('Goals_DeleteGoalConfirm_js'), '"' + goal.name + '"'));
+ $('#confirm').find('h2').text(sprintf(_pk_translate('Goals_DeleteGoalConfirm_js'), '"' + goal.name + '"'));
piwikHelper.modalConfirm('#confirm', {yes: function () {
ajaxDeleteGoal(goalId);
}});
diff --git a/plugins/Goals/templates/overview.twig b/plugins/Goals/templates/overview.twig
index 54f3219721..991663194c 100644
--- a/plugins/Goals/templates/overview.twig
+++ b/plugins/Goals/templates/overview.twig
@@ -33,6 +33,7 @@
{% endfor %}
{% if displayFullReport %}
+ {% if sum_nb_conversions != 0 %}
<h2 id='titleGoalsByDimension'>
{% if idGoal is defined %}
{{ 'Goals_GoalConversionsBy'|translate(goalName) }}
@@ -41,6 +42,7 @@
{% endif %}
</h2>
{{ goalReportsByDimension }}
+ {% endif %}
{% if userCanEditGoals %}
{% include "@Goals/add_edit_goal.twig" %}
diff --git a/plugins/ImageGraph/API.php b/plugins/ImageGraph/API.php
index 916589e683..ddc31e56b3 100644
--- a/plugins/ImageGraph/API.php
+++ b/plugins/ImageGraph/API.php
@@ -129,7 +129,8 @@ class Piwik_ImageGraph_API
$backgroundColor = Piwik_ImageGraph_API::DEFAULT_BACKGROUND_COLOR,
$gridColor = Piwik_ImageGraph_API::DEFAULT_GRID_COLOR,
$idSubtable = false,
- $legendAppendMetric = true
+ $legendAppendMetric = true,
+ $segment = false
) {
Piwik::checkUserHasViewAccess($idSite);
@@ -168,7 +169,7 @@ class Piwik_ImageGraph_API
$reportHasDimension = !empty($metadata['dimension']);
$constantRowsCount = !empty($metadata['constantRowsCount']);
- $isMultiplePeriod = Piwik_Archive::isMultiplePeriod($date, $period);
+ $isMultiplePeriod = Piwik_Period::isMultiplePeriod($date, $period);
if (!$reportHasDimension && !$isMultiplePeriod) {
throw new Exception('The graph cannot be drawn for this combination of \'date\' and \'period\' parameters.');
}
@@ -296,7 +297,7 @@ class Piwik_ImageGraph_API
$apiModule,
$apiAction,
$labels,
- $segment = false,
+ $segment,
$plottedMetric,
$languageLoaded,
$idGoal,
@@ -352,7 +353,7 @@ class Piwik_ImageGraph_API
$date,
$apiModule,
$apiAction,
- $segment = false,
+ $segment,
$apiParameters = false,
$idGoal,
$languageLoaded,
diff --git a/plugins/ImageGraph/ImageGraph.php b/plugins/ImageGraph/ImageGraph.php
index f149150d23..06e973f462 100644
--- a/plugins/ImageGraph/ImageGraph.php
+++ b/plugins/ImageGraph/ImageGraph.php
@@ -15,6 +15,11 @@ class Piwik_ImageGraph extends Piwik_Plugin
'Referers_getRefererType',
);
+ // row evolution support not yet implemented for these APIs
+ static private $REPORTS_DISABLED_EVOLUTION_GRAPH = array(
+ 'Referers_getAll',
+ );
+
public function getInformation()
{
return array(
@@ -64,7 +69,7 @@ class Piwik_ImageGraph extends Piwik_Plugin
}
// need two sets of period & date, one for single period graphs, one for multiple periods graphs
- if (Piwik_Archive::isMultiplePeriod($info['date'], $info['period'])) {
+ if (Piwik_Period::isMultiplePeriod($info['date'], $info['period'])) {
$periodForMultiplePeriodGraph = $info['period'];
$dateForMultiplePeriodGraph = $info['date'];
@@ -130,7 +135,12 @@ class Piwik_ImageGraph extends Piwik_Plugin
// thanks to API.getRowEvolution, reports with dimensions can now be plotted using an evolution graph
// however, most reports with a fixed set of dimension values are excluded
// this is done so Piwik Mobile and Scheduled Reports do not display them
- if (empty($report['constantRowsCount']) || in_array($reportUniqueId, self::$CONSTANT_ROW_COUNT_REPORT_EXCEPTIONS)) {
+ $reportWithDimensionsSupportsEvolution = empty($report['constantRowsCount']) || in_array($reportUniqueId, self::$CONSTANT_ROW_COUNT_REPORT_EXCEPTIONS);
+
+ $reportSupportsEvolution = !in_array($reportUniqueId, self::$REPORTS_DISABLED_EVOLUTION_GRAPH);
+
+ if ( $reportSupportsEvolution
+ && $reportWithDimensionsSupportsEvolution) {
$parameters['period'] = $periodForMultiplePeriodGraph;
$parameters['date'] = $dateForMultiplePeriodGraph;
$report['imageGraphEvolutionUrl'] = $urlPrefix . Piwik_Url::getQueryStringFromParameters($parameters);
diff --git a/plugins/ImageGraph/StaticGraph/HorizontalBar.php b/plugins/ImageGraph/StaticGraph/HorizontalBar.php
index 8e6d69147d..fe4cf5b3b4 100644
--- a/plugins/ImageGraph/StaticGraph/HorizontalBar.php
+++ b/plugins/ImageGraph/StaticGraph/HorizontalBar.php
@@ -173,11 +173,13 @@ class Piwik_ImageGraph_StaticGraph_HorizontalBar extends Piwik_ImageGraph_Static
- $logoHeight / 2
+ 1;
- $this->pImage->$drawingFunction(
- $gridLeftMarginBeforePadding,
- $logoYPosition,
- $logoPath
- );
+ if(method_exists($this->pImage, $drawingFunction)) {
+ $this->pImage->$drawingFunction(
+ $gridLeftMarginBeforePadding,
+ $logoYPosition,
+ $logoPath
+ );
+ }
}
}
}
diff --git a/plugins/ImageGraph/StaticGraph/PieGraph.php b/plugins/ImageGraph/StaticGraph/PieGraph.php
index d2b8df51c5..a44bf79418 100644
--- a/plugins/ImageGraph/StaticGraph/PieGraph.php
+++ b/plugins/ImageGraph/StaticGraph/PieGraph.php
@@ -114,7 +114,7 @@ abstract class Piwik_ImageGraph_StaticGraph_PieGraph extends Piwik_ImageGraph_St
$smallValuesSum += $this->ordinateSeries[$metricColumn][$ordinateValuesCount - 1];
if (($smallValuesSum / $ordinateValuesSum) > 0.01) {
$truncatedOrdinateSeries[$metricColumn][] = $smallValuesSum;
- $truncatedAbscissaSeries[] = Piwik_Translate('General_Others');
+ $truncatedAbscissaSeries[] = end($this->abscissaSeries);
}
$this->ordinateSeries = $truncatedOrdinateSeries;
diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php
index cc45c10b18..e55e75b3f1 100644
--- a/plugins/Installation/Controller.php
+++ b/plugins/Installation/Controller.php
@@ -251,19 +251,20 @@ class Piwik_Installation_Controller extends Piwik_Controller_Admin
}
$tablesInstalled = Piwik::getTablesInstalled();
- $tablesToInstall = Piwik::getTablesNames();
$view->tablesInstalled = '';
if (count($tablesInstalled) > 0) {
// we have existing tables
$view->tablesInstalled = implode(', ', $tablesInstalled);
$view->someTablesInstalled = true;
+ // remove monthly archive tables
+ $archiveTables = Piwik_DataAccess_ArchiveTableCreator::getTablesArchivesInstalled();
+ $baseTablesInstalled = count($tablesInstalled) - count($archiveTables);
$minimumCountPiwikTables = 17;
- $baseTablesInstalled = preg_grep('/archive_numeric|archive_blob/', $tablesInstalled, PREG_GREP_INVERT);
Piwik::createAccessObject();
Piwik::setUserIsSuperUser();
- if (count($baseTablesInstalled) >= $minimumCountPiwikTables &&
+ if ($baseTablesInstalled >= $minimumCountPiwikTables &&
count(Piwik_SitesManager_API::getInstance()->getAllSitesId()) > 0 &&
count(Piwik_UsersManager_API::getInstance()->getUsers()) > 0
) {
diff --git a/plugins/Live/API.php b/plugins/Live/API.php
index 11ae82c413..0cccab5d1b 100644
--- a/plugins/Live/API.php
+++ b/plugins/Live/API.php
@@ -535,13 +535,13 @@ class Piwik_Live_API
$goalDetails = Piwik_FetchAll($sql, array($idVisit));
$sql = "SELECT
- case idgoal when " . Piwik_Tracker_GoalManager::IDGOAL_CART . " then '" . Piwik_Archive::LABEL_ECOMMERCE_CART . "' else '" . Piwik_Archive::LABEL_ECOMMERCE_ORDER . "' end as type,
+ case idgoal when " . Piwik_Tracker_GoalManager::IDGOAL_CART . " then '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART . "' else '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER . "' end as type,
idorder as orderId,
- " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue') . " as revenue,
- " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_subtotal') . " as revenueSubTotal,
- " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_tax') . " as revenueTax,
- " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_shipping') . " as revenueShipping,
- " . Piwik_ArchiveProcessing_Day::getSqlRevenue('revenue_discount') . " as revenueDiscount,
+ " . Piwik_DataAccess_LogAggregator::getSqlRevenue('revenue') . " as revenue,
+ " . Piwik_DataAccess_LogAggregator::getSqlRevenue('revenue_subtotal') . " as revenueSubTotal,
+ " . Piwik_DataAccess_LogAggregator::getSqlRevenue('revenue_tax') . " as revenueTax,
+ " . Piwik_DataAccess_LogAggregator::getSqlRevenue('revenue_shipping') . " as revenueShipping,
+ " . Piwik_DataAccess_LogAggregator::getSqlRevenue('revenue_discount') . " as revenueDiscount,
items as items,
log_conversion.server_time as serverTimePretty
@@ -553,7 +553,7 @@ class Piwik_Live_API
$ecommerceDetails = Piwik_FetchAll($sql, array($idVisit));
foreach ($ecommerceDetails as &$ecommerceDetail) {
- if ($ecommerceDetail['type'] == Piwik_Archive::LABEL_ECOMMERCE_CART) {
+ if ($ecommerceDetail['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
unset($ecommerceDetail['orderId']);
unset($ecommerceDetail['revenueSubTotal']);
unset($ecommerceDetail['revenueTax']);
@@ -578,7 +578,7 @@ class Piwik_Live_API
log_action_sku.name as itemSKU,
log_action_name.name as itemName,
log_action_category.name as itemCategory,
- " . Piwik_ArchiveProcessing_Day::getSqlRevenue('price') . " as price,
+ " . Piwik_DataAccess_LogAggregator::getSqlRevenue('price') . " as price,
quantity as quantity
FROM " . Piwik_Common::prefixTable('log_conversion_item') . "
INNER JOIN " . Piwik_Common::prefixTable('log_action') . " AS log_action_sku
@@ -616,8 +616,8 @@ class Piwik_Live_API
case 'goal':
$details['icon'] = 'plugins/Zeitgeist/images/goal.png';
break;
- case Piwik_Archive::LABEL_ECOMMERCE_ORDER:
- case Piwik_Archive::LABEL_ECOMMERCE_CART:
+ case Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER:
+ case Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART:
$details['icon'] = 'plugins/Zeitgeist/images/' . $details['type'] . '.gif';
break;
case Piwik_Tracker_Action_Interface::TYPE_DOWNLOAD:
diff --git a/plugins/Live/Controller.php b/plugins/Live/Controller.php
index b049bbf80c..0d6e1e89d7 100644
--- a/plugins/Live/Controller.php
+++ b/plugins/Live/Controller.php
@@ -130,7 +130,7 @@ class Piwik_Live_Controller extends Piwik_Controller
private function setCounters($view)
{
- $segment = Piwik_Common::getRequestVar('segment', false, 'string');
+ $segment = Piwik_ViewDataTable::getRawSegmentFromRequest();
$last30min = Piwik_Live_API::getInstance()->getCounters($this->idSite, $lastMinutes = 30, $segment);
$last30min = $last30min[0];
$today = Piwik_Live_API::getInstance()->getCounters($this->idSite, $lastMinutes = 24 * 60, $segment);
diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php
index 4ceb88f9fd..aaf6904845 100644
--- a/plugins/Live/Visitor.php
+++ b/plugins/Live/Visitor.php
@@ -85,6 +85,7 @@ class Piwik_Live_Visitor
'latitude' => $this->getLatitude(),
'longitude' => $this->getLongitude(),
'provider' => $this->getProvider(),
+ 'providerName' => $this->getProviderName(),
'providerUrl' => $this->getProviderUrl(),
'referrerType' => $this->getRefererType(),
@@ -106,6 +107,7 @@ class Piwik_Live_Visitor
'browserCode' => $this->getBrowserCode(),
'browserVersion' => $this->getBrowserVersion(),
'screenType' => $this->getScreenType(),
+ 'deviceType' => $this->getDeviceType(),
'resolution' => $this->getResolution(),
'screenTypeIcon' => $this->getScreenTypeIcon(),
'plugins' => $this->getPlugins(),
@@ -342,7 +344,7 @@ class Piwik_Live_Visitor
if (Piwik_PluginsManager::getInstance()->isPluginActivated('Referers')
&& $this->getRefererType() == 'search'
) {
- $keyword = Piwik_Referers::getCleanKeyword($keyword);
+ $keyword = Piwik_Referers_API::getCleanKeyword($keyword);
}
return urldecode($keyword);
}
@@ -351,7 +353,7 @@ class Piwik_Live_Visitor
{
if ($this->getRefererType() == 'search') {
if (Piwik_PluginsManager::getInstance()->isPluginActivated('Referers')
- && $this->details['referer_keyword'] == Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED
+ && $this->details['referer_keyword'] == Piwik_Referers_API::LABEL_KEYWORD_NOT_DEFINED
) {
return 'http://piwik.org/faq/general/#faq_144';
} // Case URL is google.XX/url.... then we rewrite to the search result page url
@@ -506,6 +508,14 @@ class Piwik_Live_Visitor
return Piwik_getScreenTypeFromResolution($this->details['config_resolution']);
}
+ function getDeviceType()
+ {
+ if(Piwik_PluginsManager::getInstance()->isPluginActivated('DevicesDetection')) {
+ return Piwik_getDeviceTypeLabel($this->details['config_device_type']);
+ }
+ return false;
+ }
+
function getResolution()
{
return $this->details['config_resolution'];
@@ -515,10 +525,19 @@ class Piwik_Live_Visitor
{
return Piwik_getScreensLogo($this->getScreenType());
}
-
+
function getProvider()
{
- return Piwik_Provider_getPrettyProviderName(@$this->details['location_provider']);
+ if (isset($this->details['location_provider'])) {
+ return $this->details['location_provider'];
+ } else {
+ return Piwik_Translate('General_Unknown');
+ }
+ }
+
+ function getProviderName()
+ {
+ return Piwik_Provider_getPrettyProviderName($this->getProvider());
}
function getProviderUrl()
diff --git a/plugins/Live/templates/lastVisits.twig b/plugins/Live/templates/lastVisits.twig
index f8cc9a9703..8134b7150a 100644
--- a/plugins/Live/templates/lastVisits.twig
+++ b/plugins/Live/templates/lastVisits.twig
@@ -8,7 +8,7 @@
<div title="{{ visitor.actionDetails|length }} {{ 'Live_Actions'|translate }}" class="datetime">
<span style='display:none' class='serverTimestamp'>{{ visitor.serverTimestamp|raw }}</span>
{{ visitor.serverDatePretty }} - {{ visitor.serverTimePretty }} {% if visitor.visitDuration > 0 %}<i>({{ visitor.visitDurationPretty|raw }})</i>{% endif %}
- &nbsp;<img src="{{ visitor.countryFlag }}" title="{{ visitor.location }}, {{ 'Provider_ColumnProvider'|translate }} {{ visitor.provider }}"/>
+ &nbsp;<img src="{{ visitor.countryFlag }}" title="{{ visitor.location }}, {{ 'Provider_ColumnProvider'|translate }} {{ visitor.providerName }}"/>
&nbsp;<img src="{{ visitor.browserIcon }}" title="{{ visitor.browserName }}, {{ 'UserSettings_Plugins'|translate }}: {{ visitor.plugins }}"/>
&nbsp;<img src="{{ visitor.operatingSystemIcon }}" title="{{ visitor.operatingSystem }}, {{ visitor.resolution }}"/>
&nbsp;
diff --git a/plugins/Live/templates/visitorLog.twig b/plugins/Live/templates/visitorLog.twig
index de4147e43e..9303d526c7 100644
--- a/plugins/Live/templates/visitorLog.twig
+++ b/plugins/Live/templates/visitorLog.twig
@@ -47,7 +47,7 @@
{% for visitor in arrayDataTable %}
{% set visitorColumnContent %}
&nbsp;
- <img src="{{ visitor.columns.countryFlag }}" title="{{ visitor.columns.location }}, Provider {{ visitor.columns.provider }}"/>
+ <img src="{{ visitor.columns.countryFlag }}" title="{{ visitor.columns.location }}, Provider {{ visitor.columns.providerName }}"/>
&nbsp;
{% if visitor.columns.plugins %}
<img src="{{ visitor.columns.browserIcon }}" title="{{ 'UserSettings_BrowserWithPluginsEnabled'|translate(visitor.columns.browserName,visitor.columns.plugins) }}"/>
@@ -108,11 +108,11 @@
GPS (lat/long): {{ visitor.columns.latitude }},{{ visitor.columns.longitude }}{% endif %}">
IP: {{ visitor.columns.visitIp }}</span>{% endif %}
- {% if visitor.columns.provider is defined and visitor.columns.provider!='IP' %}
+ {% if visitor.columns.provider is defined and visitor.columns.providerName!='IP' %}
<br/>
{{ 'Provider_ColumnProvider'|translate }}:
<a href="{{ visitor.columns.providerUrl }}" target="_blank" title="{{ visitor.columns.providerUrl }}" style="text-decoration:underline;">
- {{ visitor.columns.provider }}
+ {{ visitor.columns.providerName }}
</a>
{% endif %}
{% if visitor.columns.customVariables is not empty %}
diff --git a/plugins/MobileMessaging/ReportRenderer/Exception.php b/plugins/MobileMessaging/ReportRenderer/Exception.php
index 5fa5796895..c6cdacf36b 100644
--- a/plugins/MobileMessaging/ReportRenderer/Exception.php
+++ b/plugins/MobileMessaging/ReportRenderer/Exception.php
@@ -59,7 +59,7 @@ class Piwik_MobileMessaging_ReportRenderer_Exception extends Piwik_ReportRendere
return $this->rendering;
}
- public function renderFrontPage($reportTitle, $prettyDate, $description, $reportMetadata)
+ public function renderFrontPage($reportTitle, $prettyDate, $description, $reportMetadata, $segment)
{
// nothing to do
}
diff --git a/plugins/MobileMessaging/ReportRenderer/Sms.php b/plugins/MobileMessaging/ReportRenderer/Sms.php
index d492dc1281..734484f3bf 100644
--- a/plugins/MobileMessaging/ReportRenderer/Sms.php
+++ b/plugins/MobileMessaging/ReportRenderer/Sms.php
@@ -47,7 +47,7 @@ class Piwik_MobileMessaging_ReportRenderer_Sms extends Piwik_ReportRenderer
return $this->rendering;
}
- public function renderFrontPage($reportTitle, $prettyDate, $description, $reportMetadata)
+ public function renderFrontPage($reportTitle, $prettyDate, $description, $reportMetadata, $segment)
{
// nothing to do
}
@@ -121,6 +121,14 @@ class Piwik_MobileMessaging_ReportRenderer_Sms extends Piwik_ReportRenderer
$smarty->assign("siteHasECommerce", $siteHasECommerce);
$smarty->assign("displaySiteName", $processedReport['metadata']['action'] == 'getAll');
+ // segment
+ $segment = $processedReport['segment'];
+ $displaySegment = ($segment != null);
+ $smarty->assign("displaySegment", $displaySegment);
+ if ($displaySegment) {
+ $smarty->assign("segmentName", $segment['name']);
+ }
+
$this->rendering .= $smarty->fetch(PIWIK_USER_PATH . '/plugins/MobileMessaging/templates/SMSReport.tpl');
}
}
diff --git a/plugins/MobileMessaging/templates/SMSReport.twig b/plugins/MobileMessaging/templates/SMSReport.twig
index effb58e7e7..e9027e331c 100644
--- a/plugins/MobileMessaging/templates/SMSReport.twig
+++ b/plugins/MobileMessaging/templates/SMSReport.twig
@@ -1,5 +1,9 @@
{% spaceless %}
- {{ prettyDate }}.{% endspaceless %} {% spaceless %}
+ {{ prettyDate }}
+ {% if displaySegment %}
+ ,{% endspaceless %}} {% spaceless %}}{{ segmentName }}
+ {% endif %}
+ .{% endspaceless %} {% spaceless %}
{% if reportRows is empty %}
{{ 'CoreHome_ThereIsNoDataForThisReport'|translate }}
diff --git a/plugins/MultiSites/API.php b/plugins/MultiSites/API.php
index b6b0a4d1ae..e3d398c259 100755
--- a/plugins/MultiSites/API.php
+++ b/plugins/MultiSites/API.php
@@ -218,9 +218,13 @@ class Piwik_MultiSites_API
) {
$dataTable = $dataTable->mergeChildren();
} else {
- if (!$dataTable instanceof Piwik_DataTable_Array) {
+ if (!($dataTable instanceof Piwik_DataTable_Array)
+ && $dataTable->getRowsCount() > 0
+ ) {
+ $firstSite = is_array($sites) ? reset($sites) : $sites;
+
$firstDataTableRow = $dataTable->getFirstRow();
- $firstDataTableRow->setColumn('label', $sites);
+ $firstDataTableRow->setColumn('label', $firstSite);
}
}
@@ -237,11 +241,14 @@ class Piwik_MultiSites_API
// put there is put directly in Piwik_DataTable::metadata.
$dataTable->setMetadata(self::getLastPeriodMetadataName('date'), $lastPeriod);
}
-
- $pastArchive = Piwik_Archive::build('all', $period, $strLastDate, $segment, $_restrictSitesToLogin);
+ $pastArchive = Piwik_Archive::build($sites, $period, $strLastDate, $segment, $_restrictSitesToLogin);
$pastData = $pastArchive->getDataTableFromNumeric($fieldsToGet);
-
- $pastData = $pastData->mergeChildren();
+
+ if ($pastData instanceof Piwik_DataTable_Array
+ && $multipleWebsitesRequested
+ ) {
+ $pastData = $pastData->mergeChildren();
+ }
// use past data to calculate evolution percentages
$this->calculateEvolutionPercentages($dataTable, $pastData, $apiMetrics);
@@ -312,6 +319,11 @@ class Piwik_MultiSites_API
*/
private function calculateEvolutionPercentages($currentData, $pastData, $apiMetrics)
{
+ if (get_class($currentData) != get_class($pastData)) { // sanity check for regressions
+ throw new Exception("Expected \$pastData to be of type ".get_class($currentData)." - got "
+ . get_class($pastData).".");
+ }
+
if ($currentData instanceof Piwik_DataTable_Array) {
$pastArray = $pastData->getArray();
foreach ($currentData->getArray() as $subTable) {
@@ -349,7 +361,7 @@ class Piwik_MultiSites_API
} else {
$revenueMetric = '';
if (Piwik_Common::isGoalPluginEnabled()) {
- $revenueMetric = Piwik_Goals::getRecordName(self::GOAL_REVENUE_METRIC);
+ $revenueMetric = Piwik_Goals_Archiver::getRecordName(self::GOAL_REVENUE_METRIC);
}
$totals = array();
@@ -421,7 +433,7 @@ class Piwik_MultiSites_API
$metrics[self::GOAL_REVENUE_METRIC] = array(
self::METRIC_TRANSLATION_KEY => 'Goals_ColumnRevenue',
self::METRIC_EVOLUTION_COL_NAME_KEY => self::GOAL_REVENUE_METRIC . '_evolution',
- self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_REVENUE_METRIC),
+ self::METRIC_RECORD_NAME_KEY => Piwik_Goals_Archiver::getRecordName(self::GOAL_REVENUE_METRIC),
self::METRIC_IS_ECOMMERCE_KEY => false,
);
@@ -430,7 +442,7 @@ class Piwik_MultiSites_API
$metrics[self::GOAL_CONVERSION_METRIC] = array(
self::METRIC_TRANSLATION_KEY => 'Goals_ColumnConversions',
self::METRIC_EVOLUTION_COL_NAME_KEY => self::GOAL_CONVERSION_METRIC . '_evolution',
- self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_CONVERSION_METRIC),
+ self::METRIC_RECORD_NAME_KEY => Piwik_Goals_Archiver::getRecordName(self::GOAL_CONVERSION_METRIC),
self::METRIC_IS_ECOMMERCE_KEY => false,
);
@@ -438,7 +450,7 @@ class Piwik_MultiSites_API
$metrics[self::ECOMMERCE_ORDERS_METRIC] = array(
self::METRIC_TRANSLATION_KEY => 'General_EcommerceOrders',
self::METRIC_EVOLUTION_COL_NAME_KEY => self::ECOMMERCE_ORDERS_METRIC . '_evolution',
- self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_CONVERSION_METRIC, 0),
+ self::METRIC_RECORD_NAME_KEY => Piwik_Goals_Archiver::getRecordName(self::GOAL_CONVERSION_METRIC, 0),
self::METRIC_IS_ECOMMERCE_KEY => true,
);
@@ -446,7 +458,7 @@ class Piwik_MultiSites_API
$metrics[self::ECOMMERCE_REVENUE_METRIC] = array(
self::METRIC_TRANSLATION_KEY => 'General_ProductRevenue',
self::METRIC_EVOLUTION_COL_NAME_KEY => self::ECOMMERCE_REVENUE_METRIC . '_evolution',
- self::METRIC_RECORD_NAME_KEY => Piwik_Goals::getRecordName(self::GOAL_REVENUE_METRIC, 0),
+ self::METRIC_RECORD_NAME_KEY => Piwik_Goals_Archiver::getRecordName(self::GOAL_REVENUE_METRIC, 0),
self::METRIC_IS_ECOMMERCE_KEY => true,
);
}
diff --git a/plugins/MultiSites/Controller.php b/plugins/MultiSites/Controller.php
index 151b080ac3..7439edfd3a 100644
--- a/plugins/MultiSites/Controller.php
+++ b/plugins/MultiSites/Controller.php
@@ -68,50 +68,60 @@ class Piwik_MultiSites_Controller extends Piwik_Controller
foreach ($siteIds as $idSite) {
$isEcommerceEnabled = Piwik_Site::isEcommerceEnabledFor($idSite);
- $digestableData[$idSite] = array(
- 'idsite' => $idSite,
- 'main_url' => Piwik_Site::getMainUrlFor($idSite),
- 'name' => Piwik_Site::getNameFor($idSite),
- 'visits' => 0,
- 'pageviews' => 0
+// allSites[{$i}] = new setRowData(
+// 0 {$site.idsite},
+// 1 {$site.visits},
+// 2 {$site.pageviews},
+// 3 {if empty($site.revenue)}0{else}{$site.revenue}{/if},
+// 4 '{$site.name|escape:"javascript"}',
+// 5 '{$site.main_url|escape:"javascript"}',
+// 6 '{if isset($site.visits_evolution)}{$site.visits_evolution|replace:",":"."}{/if}',
+// 7 '{if isset($site.pageviews_evolution)}{$site.pageviews_evolution|replace:",":"."}{/if}',
+// 8 '{if isset($site.revenue_evolution)}{$site.revenue_evolution|replace:",":"."}{/if}');
+
+ $siteData = array($idSite,0,0.0,
+ Piwik_Site::getMainUrlFor($idSite),
+ Piwik_Site::getNameFor($idSite),
);
if ($period != 'range') {
- $digestableData[$idSite]['visits_evolution'] = 0;
- $digestableData[$idSite]['pageviews_evolution'] = 0;
+ $siteData[6] = 0;
+ $siteData[7] = 0;
}
if ($displayRevenueColumn) {
$revenueDefault = $isEcommerceEnabled ? 0 : "'-'";
if ($period != 'range') {
- $digestableData[$idSite]['revenue_evolution'] = $revenueDefault;
+ $siteData[8] = $revenueDefault;
}
}
+ $digestableData[$idSite] = $siteData;
}
foreach ($dataTable->getRows() as $row) {
$idsite = (int)$row->getMetadata('idsite');
- $site = & $digestableData[$idsite];
+ $siteData = array();
- $site['visits'] = (int)$row->getColumn('nb_visits');
- $site['pageviews'] = (int)$row->getColumn('nb_pageviews');
+ $siteData[1] = (int)$row->getColumn('nb_visits');http://pastebin.com/raw.php?i=1dvHmEUA
+ $siteData[2] = (int)$row->getColumn('nb_pageviews');
if ($displayRevenueColumn) {
if ($row->getColumn('revenue') !== false) {
- $site['revenue'] = $row->getColumn('revenue');
+ $siteData[3] = $row->getColumn('revenue');
}
}
if ($period != 'range') {
- $site['visits_evolution'] = $row->getColumn('visits_evolution');
- $site['pageviews_evolution'] = $row->getColumn('pageviews_evolution');
+ $siteData[6] = $row->getColumn('visits_evolution');
+ $siteData[7] = $row->getColumn('pageviews_evolution');
if ($displayRevenueColumn) {
- $site['revenue_evolution'] = $row->getColumn('revenue_evolution');
+ $siteData[8] = $row->getColumn('revenue_evolution');
}
}
+ $digestableData[$idsite] = array_merge($digestableData[$idsite], $siteData);
}
$this->applyPrettyMoney($digestableData);
diff --git a/plugins/MultiSites/MultiSites.php b/plugins/MultiSites/MultiSites.php
index 763b8f4797..aa23d48040 100644
--- a/plugins/MultiSites/MultiSites.php
+++ b/plugins/MultiSites/MultiSites.php
@@ -77,7 +77,7 @@ class Piwik_MultiSites extends Piwik_Plugin
public function addTopMenu()
{
- $urlParams = array('module' => 'MultiSites', 'action' => 'index');
+ $urlParams = array('module' => 'MultiSites', 'action' => 'index', 'segment' => false);
$tooltip = Piwik_Translate('MultiSites_TopLinkTooltip');
Piwik_AddTopMenu('General_MultiSitesSummary', $urlParams, true, 3, $isHTML = false, $tooltip);
}
diff --git a/plugins/PDFReports/API.php b/plugins/PDFReports/API.php
index aacba73e00..d3b0fccb95 100644
--- a/plugins/PDFReports/API.php
+++ b/plugins/PDFReports/API.php
@@ -76,10 +76,11 @@ class Piwik_PDFReports_API
* @param string $reportFormat 'pdf', 'html' or any other format provided via the PDFReports.getReportFormats hook
* @param array $reports array of reports
* @param array $parameters array of parameters
+ * @param int $idSegment Segment Identifier
*
* @return int idReport generated
*/
- public function addReport($idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters)
+ public function addReport($idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters, $idSegment = false)
{
Piwik::checkUserIsNotAnonymous();
Piwik::checkUserHasViewAccess($idSite);
@@ -87,7 +88,7 @@ class Piwik_PDFReports_API
$currentUser = Piwik::getCurrentUserLogin();
self::ensureLanguageSetForUser($currentUser);
- self::validateCommonReportAttributes($period, $hour, $description, $reportType, $reportFormat);
+ self::validateCommonReportAttributes($period, $hour, $description, $idSegment, $reportType, $reportFormat);
// report parameters validations
$parameters = self::validateReportParameters($reportType, $parameters);
@@ -108,6 +109,7 @@ class Piwik_PDFReports_API
'idsite' => $idSite,
'login' => $currentUser,
'description' => $description,
+ 'idsegment' => $idSegment,
'period' => $period,
'hour' => $hour,
'type' => $reportType,
@@ -134,7 +136,7 @@ class Piwik_PDFReports_API
*
* @see addReport()
*/
- public function updateReport($idReport, $idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters)
+ public function updateReport($idReport, $idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters, $idSegment = false)
{
Piwik::checkUserIsNotAnonymous();
Piwik::checkUserHasViewAccess($idSite);
@@ -146,7 +148,7 @@ class Piwik_PDFReports_API
$currentUser = Piwik::getCurrentUserLogin();
self::ensureLanguageSetForUser($currentUser);
- self::validateCommonReportAttributes($period, $hour, $description, $reportType, $reportFormat);
+ self::validateCommonReportAttributes($period, $hour, $description, $idSegment, $reportType, $reportFormat);
// report parameters validations
$parameters = self::validateReportParameters($reportType, $parameters);
@@ -157,6 +159,7 @@ class Piwik_PDFReports_API
Zend_Registry::get('db')->update(Piwik_Common::prefixTable('report'),
array(
'description' => $description,
+ 'idsegment' => $idSegment,
'period' => $period,
'hour' => $hour,
'type' => $reportType,
@@ -199,10 +202,11 @@ class Piwik_PDFReports_API
* @param int $idSite If specified, will filter reports that belong to a specific idsite
* @param string $period If specified, will filter reports that are scheduled for this period (day,week,month)
* @param int $idReport If specified, will filter the report that has the given idReport
+ * @param int $idSegment If specified, will filter the report that has the given idSegment
* @return array
* @throws Exception if $idReport was specified but the report wasn't found
*/
- public function getReports($idSite = false, $period = false, $idReport = false, $ifSuperUserReturnOnlySuperUserReports = false)
+ public function getReports($idSite = false, $period = false, $idReport = false, $ifSuperUserReturnOnlySuperUserReports = false, $idSegment = false)
{
Piwik::checkUserHasSomeViewAccess();
$cacheKey = (int)$idSite . '.' . (string)$period . '.' . (int)$idReport . '.' . (int)$ifSuperUserReturnOnlySuperUserReports;
@@ -235,6 +239,10 @@ class Piwik_PDFReports_API
$sqlWhere .= " AND idreport = ?";
$bind[] = $idReport;
}
+ if (!empty($idSegment)) {
+ $sqlWhere .= " AND idsegment = ?";
+ $bind[] = $idSegment;
+ }
// Joining with the site table to work around pre-1.3 where reports could still be linked to a deleted site
$reports = Piwik_FetchAll("SELECT *
@@ -330,6 +338,7 @@ class Piwik_PDFReports_API
$prettyDate = null;
$processedReports = array();
+ $segment = self::getSegment($report['idsegment']);
foreach ($reportMetadata as $action) {
$apiModule = $action['module'];
$apiAction = $action['action'];
@@ -361,9 +370,12 @@ class Piwik_PDFReports_API
$processedReport = Piwik_API_API::getInstance()->getProcessedReport(
$idSite, $period, $date, $apiModule, $apiAction,
- $segment = false, $apiParameters, $idGoal = false, $language
+ $segment != null ? urlencode($segment['definition']) : false,
+ $apiParameters, $idGoal = false, $language
);
+ $processedReport['segment'] = $segment;
+
// TODO add static method getPrettyDate($period, $date) in Piwik_Period
$prettyDate = $processedReport['prettyDate'];
@@ -409,7 +421,7 @@ class Piwik_PDFReports_API
list($reportSubject, $reportTitle) = self::getReportSubjectAndReportTitle(Piwik_Site::getNameFor($idSite), $report['reports']);
$filename = "$reportTitle - $prettyDate - $description";
- $reportRenderer->renderFrontPage($reportTitle, $prettyDate, $description, $reportMetadata);
+ $reportRenderer->renderFrontPage($reportTitle, $prettyDate, $description, $reportMetadata, $segment);
array_walk($processedReports, array($reportRenderer, 'renderReport'));
switch ($outputType) {
@@ -429,7 +441,8 @@ class Piwik_PDFReports_API
$report['metadata'],
Piwik_ReportRenderer_Html::IMAGE_GRAPH_WIDTH,
Piwik_ReportRenderer_Html::IMAGE_GRAPH_HEIGHT,
- $report['evolutionGraph']
+ $report['evolutionGraph'],
+ $segment
);
$additionalFile['mimeType'] = 'image/png';
$additionalFile['encoding'] = Zend_Mime::ENCODING_BASE64;
@@ -609,11 +622,12 @@ class Piwik_PDFReports_API
return Piwik_Common::json_encode($requestedReports);
}
- private static function validateCommonReportAttributes($period, $hour, &$description, $reportType, $reportFormat)
+ private static function validateCommonReportAttributes($period, $hour, &$description, &$idSegment, $reportType, $reportFormat)
{
self::validateReportPeriod($period);
self::validateReportHour($hour);
self::validateAndTruncateDescription($description);
+ self::validateIdSegment($idSegment);
self::validateReportType($reportType);
self::validateReportFormat($reportType, $reportFormat);
}
@@ -633,6 +647,22 @@ class Piwik_PDFReports_API
}
}
+ private static function validateIdSegment(&$idSegment)
+ {
+ if (empty($idSegment) || (is_numeric($idSegment) && $idSegment == 0)) {
+
+ $idSegment = null;
+
+ } elseif (!is_numeric($idSegment)) {
+
+ throw new Exception('Invalid segment identifier. Should be an integer.');
+
+ } elseif (self::getSegment($idSegment) == null) {
+
+ throw new Exception('Segment with id ' . $idSegment . ' does not exist or SegmentEditor is not activated.');
+ }
+ }
+
private static function validateReportType($reportType)
{
$reportTypes = array_keys(self::getReportTypes());
@@ -740,4 +770,29 @@ class Piwik_PDFReports_API
return $recipients;
}
+
+ /**
+ * @ignore
+ */
+ static public function getSegment($idSegment)
+ {
+ if (self::isSegmentEditorActivated() && !empty($idSegment)) {
+
+ $segment = Piwik_SegmentEditor_API::getInstance()->get($idSegment);
+
+ if ($segment) {
+ return $segment;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @ignore
+ */
+ public static function isSegmentEditorActivated()
+ {
+ return Piwik_PluginsManager::getInstance()->isPluginActivated('SegmentEditor');
+ }
}
diff --git a/plugins/PDFReports/Controller.php b/plugins/PDFReports/Controller.php
index 4769364d81..51d140d727 100644
--- a/plugins/PDFReports/Controller.php
+++ b/plugins/PDFReports/Controller.php
@@ -70,6 +70,17 @@ class Piwik_PDFReports_Controller extends Piwik_Controller
$view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser();
+ $view->segmentEditorActivated = false;
+ if (Piwik_PDFReports_API::isSegmentEditorActivated()) {
+
+ $savedSegmentsById = array();
+ foreach (Piwik_SegmentEditor_API::getInstance()->getAll($this->idSite) as $savedSegment) {
+ $savedSegmentsById[$savedSegment['idsegment']] = $savedSegment['name'];
+ }
+ $view->savedSegmentsById = $savedSegmentsById;
+ $view->segmentEditorActivated = true;
+ }
+
echo $view->render();
}
}
diff --git a/plugins/PDFReports/PDFReports.php b/plugins/PDFReports/PDFReports.php
index 15d0f534a8..7ffc5c1a24 100644
--- a/plugins/PDFReports/PDFReports.php
+++ b/plugins/PDFReports/PDFReports.php
@@ -67,22 +67,23 @@ class Piwik_PDFReports extends Piwik_Plugin
public function getListHooksRegistered()
{
return array(
- 'TopMenu.add' => 'addTopMenu',
- 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'PDFReports.getReportParameters' => 'getReportParameters',
- 'PDFReports.validateReportParameters' => 'validateReportParameters',
- 'PDFReports.getReportMetadata' => 'getReportMetadata',
- 'PDFReports.getReportTypes' => 'getReportTypes',
- 'PDFReports.getReportFormats' => 'getReportFormats',
- 'PDFReports.getRendererInstance' => 'getRendererInstance',
- 'PDFReports.getReportRecipients' => 'getReportRecipients',
- 'PDFReports.processReports' => 'processReports',
- 'PDFReports.allowMultipleReports' => 'allowMultipleReports',
- 'PDFReports.sendReport' => 'sendReport',
- 'template_reportParametersPDFReports' => 'template_reportParametersPDFReports',
- 'UsersManager.deleteUser' => 'deleteUserReport',
- 'SitesManager.deleteSite' => 'deleteSiteReport',
+ 'TopMenu.add' => 'addTopMenu',
+ 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'PDFReports.getReportParameters' => 'getReportParameters',
+ 'PDFReports.validateReportParameters' => 'validateReportParameters',
+ 'PDFReports.getReportMetadata' => 'getReportMetadata',
+ 'PDFReports.getReportTypes' => 'getReportTypes',
+ 'PDFReports.getReportFormats' => 'getReportFormats',
+ 'PDFReports.getRendererInstance' => 'getRendererInstance',
+ 'PDFReports.getReportRecipients' => 'getReportRecipients',
+ 'PDFReports.processReports' => 'processReports',
+ 'PDFReports.allowMultipleReports' => 'allowMultipleReports',
+ 'PDFReports.sendReport' => 'sendReport',
+ 'template_reportParametersPDFReports' => 'template_reportParametersPDFReports',
+ 'UsersManager.deleteUser' => 'deleteUserReport',
+ 'SitesManager.deleteSite' => 'deleteSiteReport',
+ Piwik_SegmentEditor_API::DELETE_SEGMENT_EVENT => 'segmentDeletion',
);
}
@@ -310,7 +311,7 @@ class Piwik_PDFReports extends Piwik_Plugin
$filename = $notificationInfo[Piwik_PDFReports_API::FILENAME_KEY];
$additionalFiles = $notificationInfo[Piwik_PDFReports_API::ADDITIONAL_FILES_KEY];
- $periods = self::getPeriodToFrequency();
+ $periods = self::getPeriodToFrequencyAsAdjective();
$message = Piwik_Translate('PDFReports_EmailHello');
$subject = Piwik_Translate('General_Report') . ' ' . $reportTitle . " - " . $prettyDate;
@@ -323,18 +324,36 @@ class Piwik_PDFReports extends Piwik_Plugin
$attachmentName = $subject;
$mail->setFrom($fromEmailAddress, $fromEmailName);
+ $displaySegmentInfo = false;
+ $segmentInfo = null;
+ $segment = Piwik_PDFReports_API::getSegment($report['idsegment']);
+ if($segment != null) {
+ $displaySegmentInfo = true;
+ $segmentInfo = Piwik_Translate('PDFReports_SegmentAppliedToReports', $segment['name']);
+ }
+
switch ($report['format']) {
case 'html':
// Needed when using images as attachment with cid
$mail->setType(Zend_Mime::MULTIPART_RELATED);
$message .= "<br/>" . Piwik_Translate('PDFReports_PleaseFindBelow', array($periods[$report['period']], $reportTitle));
+
+ if($displaySegmentInfo) {
+ $message .= " " . $segmentInfo;
+ }
+
$mail->setBodyHtml($message . "<br/><br/>" . $contents);
break;
default:
case 'pdf':
$message .= "\n" . Piwik_Translate('PDFReports_PleaseFindAttachedFile', array($periods[$report['period']], $reportTitle));
+
+ if($displaySegmentInfo) {
+ $message .= " " . $segmentInfo;
+ }
+
$mail->setBodyText($message);
$mail->createAttachment(
$contents,
@@ -487,11 +506,33 @@ class Piwik_PDFReports extends Piwik_Plugin
}
}
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function segmentDeletion($notification)
+ {
+ $idSegment = & $notification->getNotificationObject();
+ $reportsUsingSegment = Piwik_PDFReports_API::getInstance()->getReports(false, false, false, true, $idSegment);
+
+ if (count($reportsUsingSegment) > 0) {
+
+ $reportList = '';
+ $reportNameJoinText = ' ' . Piwik_Translate('General_And') . ' ';
+ foreach ($reportsUsingSegment as $report) {
+ $reportList .= '\'' . $report['description'] . '\'' . $reportNameJoinText;
+ }
+ $reportList = rtrim($reportList, $reportNameJoinText);
+
+ $errorMessage = Piwik_Translate('PDFReports_Segment_Deletion_Error', $reportList);
+ throw new Exception($errorMessage);
+ }
+ }
+
function addTopMenu()
{
Piwik_AddTopMenu(
$this->getTopMenuTranslationKey(),
- array('module' => 'PDFReports', 'action' => 'index'),
+ array('module' => 'PDFReports', 'action' => 'index', 'segment' => false),
true,
13,
$isHTML = false,
@@ -555,6 +596,7 @@ class Piwik_PDFReports extends Piwik_Plugin
`idsite` INTEGER(11) NOT NULL,
`login` VARCHAR(100) NOT NULL,
`description` VARCHAR(255) NOT NULL,
+ `idsegment` INT(11),
`period` VARCHAR(10) NOT NULL,
`hour` tinyint NOT NULL default 0,
`type` VARCHAR(10) NOT NULL,
@@ -606,6 +648,7 @@ class Piwik_PDFReports extends Piwik_Plugin
}
/**
+ * Used in the Report Listing
* @ignore
*/
static public function getPeriodToFrequency()
@@ -617,4 +660,19 @@ class Piwik_PDFReports extends Piwik_Plugin
Piwik_ScheduledTime::PERIOD_MONTH => Piwik_Translate('General_Monthly'),
);
}
+
+ /**
+ * Used in the Report's email content, ie "monthly report"
+ * @ignore
+ */
+ static public function getPeriodToFrequencyAsAdjective()
+ {
+ return array(
+ Piwik_ScheduledTime::PERIOD_DAY => Piwik_Translate('General_DailyReport'),
+ Piwik_ScheduledTime::PERIOD_WEEK => Piwik_Translate('General_WeeklyReport'),
+ Piwik_ScheduledTime::PERIOD_MONTH => Piwik_Translate('General_MonthlyReport'),
+ Piwik_ScheduledTime::PERIOD_YEAR => Piwik_Translate('General_YearlyReport'),
+ Piwik_ScheduledTime::PERIOD_RANGE => Piwik_Translate('General_RangeReports'),
+ );
+ }
}
diff --git a/plugins/PDFReports/javascripts/pdf.js b/plugins/PDFReports/javascripts/pdf.js
index 75e3ff831b..f6f3398a1e 100644
--- a/plugins/PDFReports/javascripts/pdf.js
+++ b/plugins/PDFReports/javascripts/pdf.js
@@ -30,8 +30,9 @@ function formSetEditReport(idReport) {
toggleReportType(report.type);
$('#report_description').html(report.description);
- $('#report_type option[value=' + report.type + ']').prop('selected', 'selected');
- $('#report_period option[value=' + report.period + ']').prop('selected', 'selected');
+ $('#report_segment').find('option[value=' + report.idsegment + ']').prop('selected', 'selected');
+ $('#report_type').find('option[value=' + report.type + ']').prop('selected', 'selected');
+ $('#report_period').find('option[value=' + report.period + ']').prop('selected', 'selected');
$('#report_hour').val(report.hour);
$('[name=report_format].' + report.type + ' option[value=' + report.format + ']').prop('selected', 'selected');
@@ -61,7 +62,7 @@ function getReportAjaxRequest(idReport, defaultApiMethod) {
function toggleReportType(reportType) {
resetReportParametersFunctions[reportType]();
- $('#report_type option').each(function (index, type) {
+ $('#report_type').find('option').each(function (index, type) {
$('.' + $(type).val()).hide();
});
$('.' + reportType).show();
@@ -74,7 +75,8 @@ function initManagePdf() {
var apiParameters = getReportAjaxRequest(idReport, 'PDFReports.updateReport');
apiParameters.idReport = idReport;
apiParameters.description = $('#report_description').val();
- apiParameters.reportType = $('#report_type option:selected').val();
+ apiParameters.idSegment = $('#report_segment').find('option:selected').val();
+ apiParameters.reportType = $('#report_type').find('option:selected').val();
apiParameters.reportFormat = $('[name=report_format].' + apiParameters.reportType + ' option:selected').val();
var reports = [];
@@ -89,7 +91,7 @@ function initManagePdf() {
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams(apiParameters, 'POST');
- ajaxHandler.addParams({period: $('#report_period option:selected').val()}, 'GET');
+ ajaxHandler.addParams({period: $('#report_period').find('option:selected').val()}, 'GET');
ajaxHandler.addParams({hour: $('#report_hour').val()}, 'GET');
ajaxHandler.redirectOnSuccess();
ajaxHandler.setLoadingElement();
diff --git a/plugins/PDFReports/templates/add.twig b/plugins/PDFReports/templates/add.twig
index b41d901efc..f54859bfba 100644
--- a/plugins/PDFReports/templates/add.twig
+++ b/plugins/PDFReports/templates/add.twig
@@ -27,6 +27,25 @@
</div>
</td>
</tr>
+ {% if segmentEditorActivated %}
+ <tr>
+ <td class="first">{{ 'SegmentEditor_ChooseASegment'|translate }} </td>
+ <td>
+ <select id='report_segment'>
+ <option value="">{{ 'SegmentEditor_DefaultAllVisits'|translate }}</option>
+ {% for savedSegmentId, savedSegmentName in savedSegmentsById %}
+ <option value="{{ savedSegmentId }}">{{ savedSegmentName }}</option>
+ {% endfor %}
+ </select>
+
+ <div class="entityInlineHelp">
+ {% set SegmentEditor_DefaultAllVisits %}{{ 'SegmentEditor_DefaultAllVisits'|translate }}{% endset %}
+ {% set SegmentEditor_AddNewSegment %}{{ 'SegmentEditor_AddNewSegment'|translate }}{% endset %}
+ {{ 'PDFReports_Segment_Help'|translate('<a href="./" target="_blank">','</a>',SegmentEditor_DefaultAllVisits,SegmentEditor_AddNewSegment)|raw }}
+ </div>
+ </td>
+ </tr>
+ {% endif %}
<tr>
<td class="first">{{ 'PDFReports_EmailSchedule'|translate }}</td>
<td>
diff --git a/plugins/PDFReports/templates/list.twig b/plugins/PDFReports/templates/list.twig
index 2a8f580a24..8d39cef697 100644
--- a/plugins/PDFReports/templates/list.twig
+++ b/plugins/PDFReports/templates/list.twig
@@ -36,7 +36,14 @@
{% else %}
{% for report in reports %}
<tr>
- <td class="first">{{ report.description }}</td>
+ <td class="first">
+ {{ report.description }}
+ {% if segmentEditorActivated and report.idsegment %}
+ <div class="entityInlineHelp" style="font-size: 9pt;">
+ {{ savedSegmentsById[report.idsegment] }}
+ </div>
+ {% endif %}
+ </td>
<td>{{ periods[report.period] }}
<!-- Last sent on {{ report.ts_last_sent }} -->
</td>
@@ -63,7 +70,7 @@
</td>
<td>
{# download link #}
- <a href="{{ url({'module':'API', 'token_auth':token_auth, 'method':'PDFReports.generateReport', 'date':rawDate, 'idReport':report.idreport, 'outputType':downloadOutputType, 'language':language}) }}"
+ <a href="{{ url({'module':'API', 'segment': null, 'token_auth':token_auth, 'method':'PDFReports.generateReport', 'idReport':report.idreport, 'outputType':downloadOutputType, 'language':language}) }}"
target="_blank" name="linkDownloadReport" id="{{ report.idreport }}" class="link_but">
<img src='{{ reportFormatsByReportType[report.type][report.format] }}' border="0"/>
{{ 'General_Download'|translate }}
diff --git a/plugins/PDFReports/templates/report_parameters.twig b/plugins/PDFReports/templates/report_parameters.twig
index 0bf2e2bd14..2a6867b518 100644
--- a/plugins/PDFReports/templates/report_parameters.twig
+++ b/plugins/PDFReports/templates/report_parameters.twig
@@ -2,7 +2,7 @@
function updateEvolutionGraphParameterVisibility() {
var evolutionGraphParameterInput = $('.report_evolution_graph');
var nonApplicableDisplayFormats = ['1', '4'];
- $.inArray($('#display_format option:selected').val(), nonApplicableDisplayFormats) != -1 ?
+ $.inArray($('#display_format').find('option:selected').val(), nonApplicableDisplayFormats) != -1 ?
evolutionGraphParameterInput.hide() : evolutionGraphParameterInput.show();
}
@@ -25,7 +25,7 @@
if (reportParameters == null) return;
- $('#display_format option[value=' + reportParameters.displayFormat + ']').prop('selected', 'selected');
+ $('#display_format').find('option[value=' + reportParameters.displayFormat + ']').prop('selected', 'selected');
updateEvolutionGraphParameterVisibility();
if (reportParameters.emailMe === true)
@@ -49,7 +49,7 @@
var parameters = Object();
- parameters.displayFormat = $('#display_format option:selected').val();
+ parameters.displayFormat = $('#display_format').find('option:selected').val();
parameters.emailMe = $('#report_email_me').prop('checked');
parameters.evolutionGraph = $('#report_evolution_graph').prop('checked');
diff --git a/plugins/PrivacyManager/PrivacyManager.php b/plugins/PrivacyManager/PrivacyManager.php
index a538a96e08..dc0b436c61 100644
--- a/plugins/PrivacyManager/PrivacyManager.php
+++ b/plugins/PrivacyManager/PrivacyManager.php
@@ -332,7 +332,7 @@ class Piwik_PrivacyManager extends Piwik_Plugin
private static function getGoalMetricsToKeep()
{
// keep all goal metrics
- return array_values(Piwik_Archive::$mappingFromIdToNameGoal);
+ return array_values(Piwik_Metrics::$mappingFromIdToNameGoal);
}
/**
@@ -354,12 +354,12 @@ class Piwik_PrivacyManager extends Piwik_Plugin
foreach ($goalMetricsToKeep as $metric) {
for ($i = 1; $i <= $maxGoalId; ++$i) // maxGoalId can be 0
{
- $metricsToKeep[] = Piwik_Goals::getRecordName($metric, $i);
+ $metricsToKeep[] = Piwik_Goals_Archiver::getRecordName($metric, $i);
}
- $metricsToKeep[] = Piwik_Goals::getRecordName($metric);
- $metricsToKeep[] = Piwik_Goals::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_ORDER);
- $metricsToKeep[] = Piwik_Goals::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_CART);
+ $metricsToKeep[] = Piwik_Goals_Archiver::getRecordName($metric);
+ $metricsToKeep[] = Piwik_Goals_Archiver::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_ORDER);
+ $metricsToKeep[] = Piwik_Goals_Archiver::getRecordName($metric, Piwik_Tracker_GoalManager::IDGOAL_CART);
}
}
diff --git a/plugins/PrivacyManager/ReportsPurger.php b/plugins/PrivacyManager/ReportsPurger.php
index 61c650a613..2fbc3fb6b6 100755
--- a/plugins/PrivacyManager/ReportsPurger.php
+++ b/plugins/PrivacyManager/ReportsPurger.php
@@ -203,24 +203,23 @@ class Piwik_PrivacyManager_ReportsPurger
// reports whose creation date <= this month will be deleted
// (NOTE: we ignore how far we are in the current month)
$toRemoveDate = Piwik_Date::factory('today')->subMonth(1 + $this->deleteReportsOlderThan);
- $toRemoveYear = (int)$toRemoveDate->toString('Y');
- $toRemoveMonth = (int)$toRemoveDate->toString('m');
// find all archive tables that are older than N months
$oldNumericTables = array();
$oldBlobTables = array();
foreach (Piwik::getTablesInstalled() as $table) {
- if (preg_match("/archive_(numeric|blob)_([0-9]+)_([0-9]+)/", $table, $matches)) {
- $type = $matches[1];
- $year = (int)$matches[2];
- $month = (int)$matches[3];
-
- if (self::shouldReportBePurged($year, $month, $toRemoveDate)) {
- if ($type == "numeric") {
- $oldNumericTables[] = $table;
- } else {
- $oldBlobTables[] = $table;
- }
+ $type = Piwik_DataAccess_ArchiveTableCreator::getTypeFromTableName($table);
+ if($type === false) {
+ continue;
+ }
+ $date = Piwik_DataAccess_ArchiveTableCreator::getDateFromTableName($table);
+ list($year, $month) = explode('_', $date);
+
+ if (self::shouldReportBePurged($year, $month, $toRemoveDate)) {
+ if ($type == Piwik_DataAccess_ArchiveTableCreator::NUMERIC_TABLE) {
+ $oldNumericTables[] = $table;
+ } else {
+ $oldBlobTables[] = $table;
}
}
}
@@ -285,7 +284,7 @@ class Piwik_PrivacyManager_ReportsPurger
// if not keeping segments make sure segments w/ kept periods are also deleted
if (!$this->keepSegmentReports) {
$this->findSegmentArchives($oldNumericTables);
- $archiveIds = $this->segmentArchiveIds[$this->getArchiveTableDate($table)];
+ $archiveIds = $this->segmentArchiveIds[Piwik_DataAccess_ArchiveTableCreator::getDateFromTableName($table)];
if (!empty($archiveIds)) {
$where .= " OR idarchive IN (" . implode(',', $archiveIds) . ")";
@@ -308,7 +307,7 @@ class Piwik_PrivacyManager_ReportsPurger
}
foreach ($numericTables as $table) {
- $tableDate = $this->getArchiveTableDate($table);
+ $tableDate = Piwik_DataAccess_ArchiveTableCreator::getDateFromTableName($table);
$maxIdArchive = Piwik_FetchOne("SELECT MAX(idarchive) FROM $table");
@@ -326,12 +325,6 @@ class Piwik_PrivacyManager_ReportsPurger
}
}
- private function getArchiveTableDate($table)
- {
- preg_match("/[a-zA-Z_]+([0-9]+_[0-9]+)/", $table, $matches);
- return $matches[1];
- }
-
/**
* Utility function. Creates a new instance of ReportsPurger with the supplied array
* of settings.
diff --git a/plugins/PrivacyManager/javascripts/privacySettings.js b/plugins/PrivacyManager/javascripts/privacySettings.js
index 75de9b8caa..d420118fdc 100644
--- a/plugins/PrivacyManager/javascripts/privacySettings.js
+++ b/plugins/PrivacyManager/javascripts/privacySettings.js
@@ -95,10 +95,10 @@ $(document).ready(function () {
});
// make sure the DB size estimate is reloaded every time a delete logs/reports setting is changed
- $('#formDeleteSettings input[type=text]').each(function () {
+ $('#formDeleteSettings').find('input[type=text]').each(function () {
$(this).change(reloadDbStats);
});
- $('#formDeleteSettings input[type=checkbox]').each(function () {
+ $('#formDeleteSettings').find('input[type=checkbox]').each(function () {
$(this).click(reloadDbStats);
});
@@ -111,7 +111,7 @@ $(document).ready(function () {
// hide all confirmation texts, then show the correct one based on what
// type of deletion is enabled.
- $('#confirmDeleteSettings>h2').each(function () {
+ $('#confirmDeleteSettings').find('>h2').each(function () {
$(this).hide();
});
@@ -145,7 +145,7 @@ $(document).ready(function () {
// if any option has been modified, abort purging and instruct user to save first
var modified = false;
- $('#formDeleteSettings input').each(function () {
+ $('#formDeleteSettings').find('input').each(function () {
if (this.type === 'checkbox' || this.type === 'radio') {
modified |= this.defaultChecked !== this.checked;
} else {
diff --git a/plugins/Provider/API.php b/plugins/Provider/API.php
index 603a8dfc94..79e8fdaefb 100644
--- a/plugins/Provider/API.php
+++ b/plugins/Provider/API.php
@@ -35,8 +35,8 @@ class Piwik_Provider_API
{
Piwik::checkUserHasViewAccess($idSite);
$archive = Piwik_Archive::build($idSite, $period, $date, $segment);
- $dataTable = $archive->getDataTable('Provider_hostnameExt');
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
+ $dataTable = $archive->getDataTable(Piwik_Provider_Archiver::PROVIDER_RECORD_NAME);
+ $dataTable->filter('Sort', array(Piwik_Metrics::INDEX_NB_VISITS));
$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getHostnameUrl'));
$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_Provider_getPrettyProviderName'));
$dataTable->queueFilter('ReplaceColumnNames');
diff --git a/plugins/Provider/Archiver.php b/plugins/Provider/Archiver.php
new file mode 100644
index 0000000000..d515c5cefc
--- /dev/null
+++ b/plugins/Provider/Archiver.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_Provider
+ */
+class Piwik_Provider_Archiver extends Piwik_PluginsArchiver
+{
+ const PROVIDER_RECORD_NAME = 'Provider_hostnameExt';
+ const PROVIDER_FIELD = "location_provider";
+
+ public function archiveDay()
+ {
+ $metrics = $this->getProcessor()->getMetricsForDimension(self::PROVIDER_FIELD);
+ $tableProvider = $this->getProcessor()->getDataTableFromDataArray($metrics);
+ $this->getProcessor()->insertBlobRecord(self::PROVIDER_RECORD_NAME, $tableProvider->getSerialized($this->maximumRows, null, Piwik_Metrics::INDEX_NB_VISITS));
+ }
+
+ public function archivePeriod()
+ {
+ $this->getProcessor()->aggregateDataTableReports(array(self::PROVIDER_RECORD_NAME), $this->maximumRows);
+ }
+} \ No newline at end of file
diff --git a/plugins/Provider/Provider.php b/plugins/Provider/Provider.php
index 64d18ace74..f6d1ff1b68 100644
--- a/plugins/Provider/Provider.php
+++ b/plugins/Provider/Provider.php
@@ -115,42 +115,6 @@ class Piwik_Provider extends Piwik_Plugin
}
/**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod($notification)
- {
- $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array('Provider_hostnameExt');
- $archiveProcessing->archiveDataTable($dataTableToSum, null, $maximumRowsInDataTable);
- }
-
- /**
- * Daily archive: processes the report Visits by Provider
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function archiveDay($notification)
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $recordName = 'Provider_hostnameExt';
- $labelSQL = "log_visit.location_provider";
- $interestByProvider = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- $tableProvider = $archiveProcessing->getDataTableFromArray($interestByProvider);
- $columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
- $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
- $archiveProcessing->insertBlobRecord($recordName, $tableProvider->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
- destroy($tableProvider);
- }
-
- /**
* Logs the provider in the log_visit table
*
* @param Piwik_Event_Notification $notification notification object
@@ -250,4 +214,34 @@ class Piwik_Provider extends Piwik_Plugin
$out .= '</div>';
}
+ /**
+ * Daily archive: processes the report Visits by Provider
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function archiveDay($notification)
+ {
+ $archiveProcessor = $notification->getNotificationObject();
+
+ $archiving = new Piwik_Provider_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archiveDay();
+ }
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ * @return mixed
+ */
+ function archivePeriod($notification)
+ {
+ $archiveProcessor = $notification->getNotificationObject();
+
+ $archiving = new Piwik_Provider_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
+ }
+ }
+
+
}
diff --git a/plugins/Referers/API.php b/plugins/Referers/API.php
index 6516228fd6..b87f7d0e8e 100644
--- a/plugins/Referers/API.php
+++ b/plugins/Referers/API.php
@@ -37,7 +37,7 @@ class Piwik_Referers_API
protected function getDataTable($name, $idSite, $period, $date, $segment, $expanded = false, $idSubtable = null)
{
$dataTable = Piwik_Archive::getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded, $idSubtable);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
+ $dataTable->filter('Sort', array(Piwik_Metrics::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
$dataTable->queueFilter('ReplaceColumnNames');
return $dataTable;
}
@@ -88,7 +88,7 @@ class Piwik_Referers_API
}
// get visits by referrer type
- $dataTable = $this->getDataTable('Referers_type', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_Referers_Archiver::REFERER_TYPE_RECORD_NAME, $idSite, $period, $date, $segment);
if ($typeReferer !== false) // filter for a specific referrer type
{
@@ -112,8 +112,7 @@ class Piwik_Referers_API
*/
public function getAll($idSite, $period, $date, $segment = false)
{
- $dataTable = $this->getRefererType($idSite, $period, $date, $segment, $typeReferer = false,
- $idSubtable = false, $expanded = true);
+ $dataTable = $this->getRefererType($idSite, $period, $date, $segment, $typeReferer = false, $idSubtable = false, $expanded = true);
if ($dataTable instanceof Piwik_DataTable_Array) {
throw new Exception("Referrers.getAll with multiple sites or dates is not supported (yet).");
@@ -121,8 +120,7 @@ class Piwik_Referers_API
$dataTable = $dataTable->mergeSubtables($labelColumn = 'referrer_type', $useMetadataColumn = true);
- // presentation filters
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc'));
+ $dataTable->filter('Sort', array(Piwik_Metrics::INDEX_NB_VISITS, 'desc'));
$dataTable->queueFilter('ReplaceColumnNames');
$dataTable->queueFilter('ReplaceSummaryRowLabel');
@@ -131,17 +129,37 @@ class Piwik_Referers_API
public function getKeywords($idSite, $period, $date, $segment = false, $expanded = false)
{
- $dataTable = $this->getDataTable('Referers_searchEngineByKeyword', $idSite, $period, $date, $segment, $expanded);
+ $dataTable = $this->getDataTable(Piwik_Referers_Archiver::KEYWORDS_RECORD_NAME, $idSite, $period, $date, $segment, $expanded);
$dataTable = $this->handleKeywordNotDefined($dataTable);
return $dataTable;
}
protected function handleKeywordNotDefined($dataTable)
{
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', array('Piwik_Referers', 'getCleanKeyword')));
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', array('Piwik_Referers_API', 'getCleanKeyword')));
return $dataTable;
}
+ const LABEL_KEYWORD_NOT_DEFINED = "";
+
+ /**
+ * @ignore
+ */
+ static public function getKeywordNotDefinedString()
+ {
+ return Piwik_Translate('General_NotDefined', Piwik_Translate('Referers_ColumnKeyword'));
+ }
+
+ /**
+ * @ignore
+ */
+ static public function getCleanKeyword($label)
+ {
+ return $label == self::LABEL_KEYWORD_NOT_DEFINED
+ ? self::getKeywordNotDefinedString()
+ : $label;
+ }
+
public function getKeywordsForPageUrl($idSite, $period, $date, $url)
{
// Fetch the Top keywords for this page
@@ -194,7 +212,7 @@ class Piwik_Referers_API
public function getSearchEnginesFromKeywordId($idSite, $period, $date, $idSubtable, $segment = false)
{
- $dataTable = $this->getDataTable('Referers_searchEngineByKeyword', $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
+ $dataTable = $this->getDataTable(Piwik_Referers_Archiver::KEYWORDS_RECORD_NAME, $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getSearchEngineUrlFromName'));
$dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', 'Piwik_getSearchEngineLogoFromUrl'));
@@ -210,7 +228,7 @@ class Piwik_Referers_API
public function getSearchEngines($idSite, $period, $date, $segment = false, $expanded = false)
{
- $dataTable = $this->getDataTable('Referers_keywordBySearchEngine', $idSite, $period, $date, $segment, $expanded);
+ $dataTable = $this->getDataTable(Piwik_Referers_Archiver::SEARCH_ENGINES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded);
$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik_getSearchEngineUrlFromName'));
$dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', 'Piwik_getSearchEngineLogoFromUrl'));
return $dataTable;
@@ -218,7 +236,7 @@ class Piwik_Referers_API
public function getKeywordsFromSearchEngineId($idSite, $period, $date, $idSubtable, $segment = false)
{
- $dataTable = $this->getDataTable('Referers_keywordBySearchEngine', $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
+ $dataTable = $this->getDataTable(Piwik_Referers_Archiver::SEARCH_ENGINES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
// get the search engine and create the URL to the search result page
$searchEngines = $this->getSearchEngines($idSite, $period, $date, $segment);
@@ -249,25 +267,25 @@ class Piwik_Referers_API
public function getCampaigns($idSite, $period, $date, $segment = false, $expanded = false)
{
- $dataTable = $this->getDataTable('Referers_keywordByCampaign', $idSite, $period, $date, $segment, $expanded);
+ $dataTable = $this->getDataTable(Piwik_Referers_Archiver::CAMPAIGNS_RECORD_NAME, $idSite, $period, $date, $segment, $expanded);
return $dataTable;
}
public function getKeywordsFromCampaignId($idSite, $period, $date, $idSubtable, $segment = false)
{
- $dataTable = $this->getDataTable('Referers_keywordByCampaign', $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
+ $dataTable = $this->getDataTable(Piwik_Referers_Archiver::CAMPAIGNS_RECORD_NAME, $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
return $dataTable;
}
public function getWebsites($idSite, $period, $date, $segment = false, $expanded = false)
{
- $dataTable = $this->getDataTable('Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded);
+ $dataTable = $this->getDataTable(Piwik_Referers_Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded);
return $dataTable;
}
public function getUrlsFromWebsiteId($idSite, $period, $date, $idSubtable, $segment = false)
{
- $dataTable = $this->getDataTable('Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
+ $dataTable = $this->getDataTable(Piwik_Referers_Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
// the htmlspecialchars_decode call is for BC for before 1.1
// as the Referer URL was previously encoded in the log tables, but is now recorded raw
$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', create_function('$label', 'return htmlspecialchars_decode($label);')));
@@ -290,7 +308,7 @@ class Piwik_Referers_API
{
require PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php';
- $dataTable = $this->getDataTable('Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded);
+ $dataTable = $this->getDataTable( Piwik_Referers_Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded);
$dataTable->filter('ColumnCallbackDeleteRow', array('label', 'Piwik_Referrers_isSocialUrl'));
@@ -323,8 +341,7 @@ class Piwik_Referers_API
{
require PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php';
- $dataTable = $this->getDataTable(
- 'Referers_urlByWebsite', $idSite, $period, $date, $segment, $expanded = true);
+ $dataTable = $this->getDataTable( Piwik_Referers_Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded = true);
// get the social network domain referred to by $idSubtable
$social = false;
@@ -350,7 +367,7 @@ class Piwik_Referers_API
// prettify the DataTable
$dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_Referrers_removeUrlProtocol'));
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
+ $dataTable->filter('Sort', array(Piwik_Metrics::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
$dataTable->queueFilter('ReplaceColumnNames');
return $dataTable;
@@ -358,27 +375,27 @@ class Piwik_Referers_API
public function getNumberOfDistinctSearchEngines($idSite, $period, $date, $segment = false)
{
- return $this->getNumeric('Referers_distinctSearchEngines', $idSite, $period, $date, $segment);
+ return $this->getNumeric(Piwik_Referers_Archiver::METRIC_DISTINCT_SEARCH_ENGINE_RECORD_NAME, $idSite, $period, $date, $segment);
}
public function getNumberOfDistinctKeywords($idSite, $period, $date, $segment = false)
{
- return $this->getNumeric('Referers_distinctKeywords', $idSite, $period, $date, $segment);
+ return $this->getNumeric(Piwik_Referers_Archiver::METRIC_DISTINCT_KEYWORD_RECORD_NAME, $idSite, $period, $date, $segment);
}
public function getNumberOfDistinctCampaigns($idSite, $period, $date, $segment = false)
{
- return $this->getNumeric('Referers_distinctCampaigns', $idSite, $period, $date, $segment);
+ return $this->getNumeric(Piwik_Referers_Archiver::METRIC_DISTINCT_CAMPAIGN_RECORD_NAME, $idSite, $period, $date, $segment);
}
public function getNumberOfDistinctWebsites($idSite, $period, $date, $segment = false)
{
- return $this->getNumeric('Referers_distinctWebsites', $idSite, $period, $date, $segment);
+ return $this->getNumeric(Piwik_Referers_Archiver::METRIC_DISTINCT_WEBSITE_RECORD_NAME, $idSite, $period, $date, $segment);
}
public function getNumberOfDistinctWebsitesUrls($idSite, $period, $date, $segment = false)
{
- return $this->getNumeric('Referers_distinctWebsitesUrls', $idSite, $period, $date, $segment);
+ return $this->getNumeric(Piwik_Referers_Archiver::METRIC_DISTINCT_URLS_RECORD_NAME, $idSite, $period, $date, $segment);
}
private function getNumeric($name, $idSite, $period, $date, $segment)
diff --git a/plugins/Referers/Archiver.php b/plugins/Referers/Archiver.php
new file mode 100644
index 0000000000..6e8152ed5e
--- /dev/null
+++ b/plugins/Referers/Archiver.php
@@ -0,0 +1,265 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_Referers
+ */
+
+class Piwik_Referers_Archiver extends Piwik_PluginsArchiver
+{
+ const SEARCH_ENGINES_RECORD_NAME = 'Referers_keywordBySearchEngine';
+ const KEYWORDS_RECORD_NAME = 'Referers_searchEngineByKeyword';
+ const CAMPAIGNS_RECORD_NAME = 'Referers_keywordByCampaign';
+ const WEBSITES_RECORD_NAME = 'Referers_urlByWebsite';
+ const REFERER_TYPE_RECORD_NAME = 'Referers_type';
+ const METRIC_DISTINCT_SEARCH_ENGINE_RECORD_NAME = 'Referers_distinctSearchEngines';
+ const METRIC_DISTINCT_KEYWORD_RECORD_NAME = 'Referers_distinctKeywords';
+ const METRIC_DISTINCT_CAMPAIGN_RECORD_NAME = 'Referers_distinctCampaigns';
+ const METRIC_DISTINCT_WEBSITE_RECORD_NAME = 'Referers_distinctWebsites';
+ const METRIC_DISTINCT_URLS_RECORD_NAME = 'Referers_distinctWebsitesUrls';
+ protected $columnToSortByBeforeTruncation;
+ protected $maximumRowsInDataTableLevelZero;
+ protected $maximumRowsInSubDataTable;
+ /* @var array[Piwik_DataArray] $arrays */
+ protected $arrays = array();
+ protected $distinctUrls = array();
+
+ function __construct($processor)
+ {
+ parent::__construct($processor);
+ $this->columnToSortByBeforeTruncation = Piwik_Metrics::INDEX_NB_VISITS;
+ $this->maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_referers'];
+ $this->maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_referers'];
+ }
+
+ public function archiveDay()
+ {
+ foreach ($this->getRecordNames() as $record) {
+ $this->arrays[$record] = new Piwik_DataArray();
+ }
+ $query = $this->getLogAggregator()->queryVisitsByDimension(array("referer_type", "referer_name", "referer_keyword", "referer_url"));
+ $this->aggregateFromVisits($query);
+
+ $query = $this->getLogAggregator()->queryConversionsByDimension(array("referer_type", "referer_name", "referer_keyword"));
+ $this->aggregateFromConversions($query);
+
+ Piwik_PostEvent('Referers.archiveDay', $this);
+ $this->recordDayReports();
+ }
+
+ protected function getRecordNames()
+ {
+ return array(
+ self::REFERER_TYPE_RECORD_NAME,
+ self::KEYWORDS_RECORD_NAME,
+ self::SEARCH_ENGINES_RECORD_NAME,
+ self::WEBSITES_RECORD_NAME,
+ self::CAMPAIGNS_RECORD_NAME,
+ );
+ }
+
+ protected function aggregateFromVisits($query)
+ {
+ while ($row = $query->fetch()) {
+ $this->makeRefererTypeNonEmpty($row);
+ $this->aggregateVisit($row);
+ }
+ }
+
+ protected function makeRefererTypeNonEmpty(&$row)
+ {
+ if (empty($row['referer_type'])) {
+ $row['referer_type'] = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
+ }
+ }
+
+ protected function aggregateVisit($row)
+ {
+ switch ($row['referer_type']) {
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ if (empty($row['referer_keyword'])) {
+ $row['referer_keyword'] = Piwik_Referers_API::LABEL_KEYWORD_NOT_DEFINED;
+ }
+ $searchEnginesArray = $this->getDataArray(self::SEARCH_ENGINES_RECORD_NAME);
+ $searchEnginesArray->sumMetricsVisits($row['referer_name'], $row);
+ $searchEnginesArray->sumMetricsVisitsPivot($row['referer_name'], $row['referer_keyword'], $row);
+ $keywordsDataArray = $this->getDataArray(self::KEYWORDS_RECORD_NAME);
+ $keywordsDataArray->sumMetricsVisits($row['referer_keyword'], $row);
+ $keywordsDataArray->sumMetricsVisitsPivot($row['referer_keyword'], $row['referer_name'], $row);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+ $this->getDataArray(self::WEBSITES_RECORD_NAME)->sumMetricsVisits($row['referer_name'], $row);
+ $this->getDataArray(self::WEBSITES_RECORD_NAME)->sumMetricsVisitsPivot($row['referer_name'], $row['referer_url'], $row);
+
+ $urlHash = substr(md5($row['referer_url']), 0, 10);
+ if (!isset($this->distinctUrls[$urlHash])) {
+ $this->distinctUrls[$urlHash] = true;
+ }
+ break;
+
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ if (!empty($row['referer_keyword'])) {
+ $this->getDataArray(self::CAMPAIGNS_RECORD_NAME)->sumMetricsVisitsPivot($row['referer_name'], $row['referer_keyword'], $row);
+ }
+ $this->getDataArray(self::CAMPAIGNS_RECORD_NAME)->sumMetricsVisits($row['referer_name'], $row);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
+ // direct entry are aggregated below in $this->metricsByType array
+ break;
+
+ default:
+ throw new Exception("Non expected referer_type = " . $row['referer_type']);
+ break;
+ }
+ $this->getDataArray(self::REFERER_TYPE_RECORD_NAME)->sumMetricsVisits($row['referer_type'], $row);
+ }
+
+ /**
+ * @param $name
+ * @return Piwik_DataArray
+ */
+ protected function getDataArray($name)
+ {
+ return $this->arrays[$name];
+ }
+
+ protected function aggregateFromConversions($query)
+ {
+ if ($query === false) {
+ return;
+ }
+ while ($row = $query->fetch()) {
+ $this->makeRefererTypeNonEmpty($row);
+
+ $skipAggregateByType = $this->aggregateConversion($row);
+ if (!$skipAggregateByType) {
+ $this->getDataArray(self::REFERER_TYPE_RECORD_NAME)->sumMetricsGoals($row['referer_type'], $row);
+ }
+ }
+
+ foreach ($this->arrays as $dataArray) {
+ /* @var Piwik_DataArray $dataArray */
+ $dataArray->enrichMetricsWithConversions();
+ }
+ }
+
+ protected function aggregateConversion($row)
+ {
+ $skipAggregateByType = false;
+ switch ($row['referer_type']) {
+ case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
+ if (empty($row['referer_keyword'])) {
+ $row['referer_keyword'] = Piwik_Referers_API::LABEL_KEYWORD_NOT_DEFINED;
+ }
+
+ $this->getDataArray(self::SEARCH_ENGINES_RECORD_NAME)->sumMetricsGoals($row['referer_name'], $row);
+ $this->getDataArray(self::KEYWORDS_RECORD_NAME)->sumMetricsGoals($row['referer_keyword'], $row);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_WEBSITE:
+ $this->getDataArray(self::WEBSITES_RECORD_NAME)->sumMetricsGoals($row['referer_name'], $row);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_CAMPAIGN:
+ if (!empty($row['referer_keyword'])) {
+ $this->getDataArray(self::CAMPAIGNS_RECORD_NAME)->sumMetricsGoalsPivot($row['referer_name'], $row['referer_keyword'], $row);
+ }
+ $this->getDataArray(self::CAMPAIGNS_RECORD_NAME)->sumMetricsGoals($row['referer_name'], $row);
+ break;
+
+ case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
+ // Direct entry, no sub dimension
+ break;
+
+ default:
+ // The referer type is user submitted for goal conversions, we ignore any malformed value
+ // Continue to the next while iteration
+ $skipAggregateByType = true;
+ break;
+ }
+ return $skipAggregateByType;
+ }
+
+ /**
+ * Records the daily stats (numeric or datatable blob) into the archive tables.
+ *
+ * @param Piwik_ArchiveProcessor $this->getProcessor()
+ */
+ protected function recordDayReports()
+ {
+ $this->recordDayNumeric();
+ $this->recordDayBlobs();
+ }
+
+ protected function recordDayNumeric()
+ {
+ $numericRecords = array(
+ self::METRIC_DISTINCT_SEARCH_ENGINE_RECORD_NAME => count($this->getDataArray(self::SEARCH_ENGINES_RECORD_NAME)),
+ self::METRIC_DISTINCT_KEYWORD_RECORD_NAME => count($this->getDataArray(self::KEYWORDS_RECORD_NAME)),
+ self::METRIC_DISTINCT_CAMPAIGN_RECORD_NAME => count($this->getDataArray(self::CAMPAIGNS_RECORD_NAME)),
+ self::METRIC_DISTINCT_WEBSITE_RECORD_NAME => count($this->getDataArray(self::WEBSITES_RECORD_NAME)),
+ self::METRIC_DISTINCT_URLS_RECORD_NAME => count($this->distinctUrls),
+ );
+
+ $this->getProcessor()->insertNumericRecords($numericRecords);
+ }
+
+ protected function recordDayBlobs()
+ {
+ foreach ($this->getRecordNames() as $recordName) {
+ $dataArray = $this->getDataArray($recordName);
+ $table = $this->getProcessor()->getDataTableFromDataArray($dataArray);
+ $blob = $table->getSerialized($this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation);
+ $this->getProcessor()->insertBlobRecord($recordName, $blob);
+ }
+ }
+
+ public function archivePeriod()
+ {
+ $dataTableToSum = $this->getRecordNames();
+ $nameToCount = $this->getProcessor()->aggregateDataTableReports($dataTableToSum, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation);
+
+ $mappingFromArchiveName = array(
+ self::METRIC_DISTINCT_SEARCH_ENGINE_RECORD_NAME =>
+ array('typeCountToUse' => 'level0',
+ 'nameTableToUse' => self::SEARCH_ENGINES_RECORD_NAME,
+ ),
+ self::METRIC_DISTINCT_KEYWORD_RECORD_NAME =>
+ array('typeCountToUse' => 'level0',
+ 'nameTableToUse' => self::KEYWORDS_RECORD_NAME,
+ ),
+ self::METRIC_DISTINCT_CAMPAIGN_RECORD_NAME =>
+ array('typeCountToUse' => 'level0',
+ 'nameTableToUse' => self::CAMPAIGNS_RECORD_NAME,
+ ),
+ self::METRIC_DISTINCT_WEBSITE_RECORD_NAME =>
+ array('typeCountToUse' => 'level0',
+ 'nameTableToUse' => self::WEBSITES_RECORD_NAME,
+ ),
+ self::METRIC_DISTINCT_URLS_RECORD_NAME =>
+ array('typeCountToUse' => 'recursive',
+ 'nameTableToUse' => self::WEBSITES_RECORD_NAME,
+ ),
+ );
+
+ foreach ($mappingFromArchiveName as $name => $infoMapping) {
+ $typeCountToUse = $infoMapping['typeCountToUse'];
+ $nameTableToUse = $infoMapping['nameTableToUse'];
+
+ if ($typeCountToUse == 'recursive') {
+
+ $countValue = $nameToCount[$nameTableToUse]['recursive']
+ - $nameToCount[$nameTableToUse]['level0'];
+ } else {
+ $countValue = $nameToCount[$nameTableToUse]['level0'];
+ }
+ $this->getProcessor()->insertNumericRecord($name, $countValue);
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/Referers/Controller.php b/plugins/Referers/Controller.php
index 6aee58ed5c..60259d57aa 100644
--- a/plugins/Referers/Controller.php
+++ b/plugins/Referers/Controller.php
@@ -437,7 +437,7 @@ class Piwik_Referers_Controller extends Piwik_Controller
$value = 0;
$row = $dataTableReferersType->getRowFromLabel($columnId);
if ($row !== false) {
- $value = $row->getColumn(Piwik_Archive::INDEX_NB_VISITS);
+ $value = $row->getColumn(Piwik_Metrics::INDEX_NB_VISITS);
}
$return[$nameVar] = $value;
}
diff --git a/plugins/Referers/Referers.php b/plugins/Referers/Referers.php
index 3e40f2d9a9..2c45b57592 100644
--- a/plugins/Referers/Referers.php
+++ b/plugins/Referers/Referers.php
@@ -19,21 +19,14 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/Referers/functions.php';
*/
class Piwik_Referers extends Piwik_Plugin
{
- public $archiveProcessing;
- protected $columnToSortByBeforeTruncation;
- protected $maximumRowsInDataTableLevelZero;
- protected $maximumRowsInSubDataTable;
-
public function getInformation()
{
- $info = array(
+ return array(
'description' => Piwik_Translate('Referers_PluginDescription'),
'author' => 'Piwik',
'author_homepage' => 'http://piwik.org/',
'version' => Piwik_Version::VERSION,
);
-
- return $info;
}
function getListHooksRegistered()
@@ -234,7 +227,7 @@ class Piwik_Referers extends Piwik_Plugin
Piwik_AddWidget('Referers_Referers', 'Referers_WidgetCampaigns', 'Referers', 'getCampaigns');
Piwik_AddWidget('Referers_Referers', 'Referers_WidgetOverview', 'Referers', 'getRefererType');
Piwik_AddWidget('Referers_Referers', 'Referers_WidgetGetAll', 'Referers', 'getAll');
- if (Piwik_Archive::isSegmentationEnabled()) {
+ if (Piwik::isSegmentationEnabled()) {
Piwik_AddWidget('SEO', 'Referers_WidgetTopKeywordsForPages', 'Referers', 'getKeywordsForPage');
}
}
@@ -289,300 +282,33 @@ class Piwik_Referers extends Piwik_Plugin
));
}
- function __construct()
- {
- $this->columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
- $this->maximumRowsInDataTableLevelZero = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_referers'];
- $this->maximumRowsInSubDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_referers'];
- }
-
- /**
- * Period archiving: sums up daily stats and sums report tables,
- * making sure that tables are still truncated.
- *
- * @param Piwik_Event_Notification $notification notification object
- * @return void
- */
- function archivePeriod($notification)
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array(
- 'Referers_type',
- 'Referers_keywordBySearchEngine',
- 'Referers_searchEngineByKeyword',
- 'Referers_keywordByCampaign',
- 'Referers_urlByWebsite',
- );
- $nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum, null, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation);
-
- $mappingFromArchiveName = array(
- 'Referers_distinctSearchEngines' =>
- array('typeCountToUse' => 'level0',
- 'nameTableToUse' => 'Referers_keywordBySearchEngine',
- ),
- 'Referers_distinctKeywords' =>
- array('typeCountToUse' => 'level0',
- 'nameTableToUse' => 'Referers_searchEngineByKeyword',
- ),
- 'Referers_distinctCampaigns' =>
- array('typeCountToUse' => 'level0',
- 'nameTableToUse' => 'Referers_keywordByCampaign',
- ),
- 'Referers_distinctWebsites' =>
- array('typeCountToUse' => 'level0',
- 'nameTableToUse' => 'Referers_urlByWebsite',
- ),
- 'Referers_distinctWebsitesUrls' =>
- array('typeCountToUse' => 'recursive',
- 'nameTableToUse' => 'Referers_urlByWebsite',
- ),
- );
-
- foreach ($mappingFromArchiveName as $name => $infoMapping) {
- $typeCountToUse = $infoMapping['typeCountToUse'];
- $nameTableToUse = $infoMapping['nameTableToUse'];
-
- if ($typeCountToUse == 'recursive') {
-
- $countValue = $nameToCount[$nameTableToUse]['recursive']
- - $nameToCount[$nameTableToUse]['level0'];
- } else {
- $countValue = $nameToCount[$nameTableToUse]['level0'];
- }
- $archiveProcessing->insertNumericRecord($name, $countValue);
- }
- }
-
- const LABEL_KEYWORD_NOT_DEFINED = "";
-
- static public function getKeywordNotDefinedString()
- {
- return Piwik_Translate('General_NotDefined', Piwik_Translate('Referers_ColumnKeyword'));
- }
-
- static public function getCleanKeyword($label)
- {
- return $label == Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED
- ? self::getKeywordNotDefinedString()
- : $label;
- }
-
/**
* Hooks on daily archive to trigger various log processing
*
* @param Piwik_Event_Notification $notification notification object
- * @return void
*/
public function archiveDay($notification)
{
- /**
- * @var Piwik_ArchiveProcessing_Day
- */
- $this->archiveProcessing = $notification->getNotificationObject();
- if (!$this->archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveDayAggregateVisits($this->archiveProcessing);
- $this->archiveDayAggregateGoals($this->archiveProcessing);
- Piwik_PostEvent('Referers.archiveDay', $this);
- $this->archiveDayRecordInDatabase($this->archiveProcessing);
- $this->cleanup();
- }
-
- protected function cleanup()
- {
- destroy($this->interestBySearchEngine);
- destroy($this->interestByKeyword);
- destroy($this->interestBySearchEngineAndKeyword);
- destroy($this->interestByKeywordAndSearchEngine);
- destroy($this->interestByWebsite);
- destroy($this->interestByWebsiteAndUrl);
- destroy($this->interestByCampaignAndKeyword);
- destroy($this->interestByCampaign);
- destroy($this->interestByType);
- destroy($this->distinctUrls);
- }
-
- /**
- * Daily archive: processes all Referers reports, eg. Visits by Keyword,
- * Visits by websites, etc.
- *
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @throws Exception
- * @return void
- */
- protected function archiveDayAggregateVisits(Piwik_ArchiveProcessing_Day $archiveProcessing)
- {
- $dimension = array("referer_type", "referer_name", "referer_keyword", "referer_url");
- $query = $archiveProcessing->queryVisitsByDimension($dimension);
-
- $this->interestBySearchEngine =
- $this->interestByKeyword =
- $this->interestBySearchEngineAndKeyword =
- $this->interestByKeywordAndSearchEngine =
- $this->interestByWebsite =
- $this->interestByWebsiteAndUrl =
- $this->interestByCampaignAndKeyword =
- $this->interestByCampaign =
- $this->interestByType =
- $this->distinctUrls = array();
- while ($row = $query->fetch()) {
- if (empty($row['referer_type'])) {
- $row['referer_type'] = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
- } else {
- switch ($row['referer_type']) {
- case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
- if (empty($row['referer_keyword'])) {
- $row['referer_keyword'] = self::LABEL_KEYWORD_NOT_DEFINED;
- }
- if (!isset($this->interestBySearchEngine[$row['referer_name']])) $this->interestBySearchEngine[$row['referer_name']] = $archiveProcessing->getNewInterestRow();
- if (!isset($this->interestByKeyword[$row['referer_keyword']])) $this->interestByKeyword[$row['referer_keyword']] = $archiveProcessing->getNewInterestRow();
- if (!isset($this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']])) $this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']] = $archiveProcessing->getNewInterestRow();
- if (!isset($this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']])) $this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']] = $archiveProcessing->getNewInterestRow();
-
- $archiveProcessing->updateInterestStats($row, $this->interestBySearchEngine[$row['referer_name']]);
- $archiveProcessing->updateInterestStats($row, $this->interestByKeyword[$row['referer_keyword']]);
- $archiveProcessing->updateInterestStats($row, $this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']]);
- $archiveProcessing->updateInterestStats($row, $this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_WEBSITE:
-
- if (!isset($this->interestByWebsite[$row['referer_name']])) $this->interestByWebsite[$row['referer_name']] = $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats($row, $this->interestByWebsite[$row['referer_name']]);
-
- if (!isset($this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']])) $this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']] = $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats($row, $this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']]);
+ $archiveProcessor = $notification->getNotificationObject();
- if (!isset($this->distinctUrls[$row['referer_url']])) {
- $this->distinctUrls[$row['referer_url']] = true;
- }
- break;
-
- case Piwik_Common::REFERER_TYPE_CAMPAIGN:
- if (!empty($row['referer_keyword'])) {
- if (!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']] = $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats($row, $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']]);
- }
- if (!isset($this->interestByCampaign[$row['referer_name']])) $this->interestByCampaign[$row['referer_name']] = $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats($row, $this->interestByCampaign[$row['referer_name']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
- // direct entry are aggregated below in $this->interestByType array
- break;
-
- default:
- throw new Exception("Non expected referer_type = " . $row['referer_type']);
- break;
- }
- }
- if (!isset($this->interestByType[$row['referer_type']])) $this->interestByType[$row['referer_type']] = $archiveProcessing->getNewInterestRow();
- $archiveProcessing->updateInterestStats($row, $this->interestByType[$row['referer_type']]);
+ $archiving = new Piwik_Referers_Archiver($archiveProcessor);
+ if ($archiving->shouldArchive()) {
+ $archiving->archiveDay();
}
}
/**
- * Daily Goal archiving: processes reports of Goal conversions by Keyword,
- * Goal conversions by Referer Websites, etc.
- *
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @return void
- */
- protected function archiveDayAggregateGoals($archiveProcessing)
- {
- $query = $archiveProcessing->queryConversionsByDimension(array("referer_type", "referer_name", "referer_keyword"));
-
- if ($query === false) return;
- while ($row = $query->fetch()) {
- if (empty($row['referer_type'])) {
- $row['referer_type'] = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY;
- } else {
- switch ($row['referer_type']) {
- case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE:
- if (empty($row['referer_keyword'])) {
- $row['referer_keyword'] = self::LABEL_KEYWORD_NOT_DEFINED;
- }
- if (!isset($this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- if (!isset($this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
-
- $archiveProcessing->updateGoalStats($row, $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- $archiveProcessing->updateGoalStats($row, $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_WEBSITE:
- if (!isset($this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats($row, $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_CAMPAIGN:
- if (!empty($row['referer_keyword'])) {
- if (!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats($row, $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- }
- if (!isset($this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats($row, $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- break;
-
- case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY:
- // Direct entry, no sub dimension
- break;
-
- default:
- // The referer type is user submitted for goal conversions, we ignore any malformed value
- // Continue to the next while iteration
- continue 2;
- break;
- }
- }
- if (!isset($this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats($row, $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
- }
-
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByType);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestBySearchEngine);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByKeyword);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByWebsite);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByCampaign);
- $archiveProcessing->enrichConversionsByLabelArrayHasTwoLevels($this->interestByCampaignAndKeyword);
- }
-
- /**
- * Records the daily stats (numeric or datatable blob) into the archive tables.
+ * Period archiving: sums up daily stats and sums report tables,
+ * making sure that tables are still truncated.
*
- * @param Piwik_ArchiveProcessing $archiveProcessing
- * @return void
+ * @param Piwik_Event_Notification $notification notification object
*/
- protected function archiveDayRecordInDatabase($archiveProcessing)
+ function archivePeriod($notification)
{
- $numericRecords = array(
- 'Referers_distinctSearchEngines' => count($this->interestBySearchEngineAndKeyword),
- 'Referers_distinctKeywords' => count($this->interestByKeywordAndSearchEngine),
- 'Referers_distinctCampaigns' => count($this->interestByCampaign),
- 'Referers_distinctWebsites' => count($this->interestByWebsite),
- 'Referers_distinctWebsitesUrls' => count($this->distinctUrls),
- );
-
- foreach ($numericRecords as $name => $value) {
- $archiveProcessing->insertNumericRecord($name, $value);
- }
-
- $dataTable = $archiveProcessing->getDataTableSerialized($this->interestByType);
- $archiveProcessing->insertBlobRecord('Referers_type', $dataTable);
- destroy($dataTable);
-
- $blobRecords = array(
- 'Referers_keywordBySearchEngine' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestBySearchEngineAndKeyword, $this->interestBySearchEngine),
- 'Referers_searchEngineByKeyword' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByKeywordAndSearchEngine, $this->interestByKeyword),
- 'Referers_keywordByCampaign' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByCampaignAndKeyword, $this->interestByCampaign),
- 'Referers_urlByWebsite' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByWebsiteAndUrl, $this->interestByWebsite),
- );
- foreach ($blobRecords as $recordName => $table) {
- $blob = $table->getSerialized($this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation);
- $archiveProcessing->insertBlobRecord($recordName, $blob);
- destroy($table);
+ $archiveProcessor = $notification->getNotificationObject();
+ $archiving = new Piwik_Referers_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
}
}
}
diff --git a/plugins/Referers/functions.php b/plugins/Referers/functions.php
index c7d69386c7..46d9e18000 100644
--- a/plugins/Referers/functions.php
+++ b/plugins/Referers/functions.php
@@ -178,7 +178,7 @@ function Piwik_getSearchEngineHostPathFromUrl($url)
*/
function Piwik_getSearchEngineUrlFromUrlAndKeyword($url, $keyword)
{
- if ($keyword === Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED) {
+ if ($keyword === Piwik_Referers_API::LABEL_KEYWORD_NOT_DEFINED) {
return 'http://piwik.org/faq/general/#faq_144';
}
$searchEngineUrls = Piwik_Common::getSearchEngineUrls();
diff --git a/plugins/Referers/images/searchEngines/search.imesh.com.png b/plugins/Referers/images/searchEngines/search.imesh.com.png
new file mode 100644
index 0000000000..8d8f1a782e
--- /dev/null
+++ b/plugins/Referers/images/searchEngines/search.imesh.com.png
Binary files differ
diff --git a/plugins/Referers/images/searchEngines/search.snap.do.png b/plugins/Referers/images/searchEngines/search.snap.do.png
new file mode 100644
index 0000000000..fc8131e8bc
--- /dev/null
+++ b/plugins/Referers/images/searchEngines/search.snap.do.png
Binary files differ
diff --git a/plugins/Referers/images/searchEngines/www.talimba.com.png b/plugins/Referers/images/searchEngines/www.talimba.com.png
new file mode 100644
index 0000000000..4403a249b1
--- /dev/null
+++ b/plugins/Referers/images/searchEngines/www.talimba.com.png
Binary files differ
diff --git a/plugins/Referers/images/searchEngines/www.trusted--search.com.png b/plugins/Referers/images/searchEngines/www.trusted-search.com.png
index 0740cef1a7..0740cef1a7 100644
--- a/plugins/Referers/images/searchEngines/www.trusted--search.com.png
+++ b/plugins/Referers/images/searchEngines/www.trusted-search.com.png
Binary files differ
diff --git a/plugins/SEO/API.php b/plugins/SEO/API.php
index 26ac00c5bd..bc94a2663d 100644
--- a/plugins/SEO/API.php
+++ b/plugins/SEO/API.php
@@ -100,8 +100,6 @@ class Piwik_SEO_API
$data[Piwik_Translate('SEO_Dmoz')] = $dmozRank;
}
- $dataTable = new Piwik_DataTable();
- $dataTable->addRowsFromArrayWithIndexLabel($data);
- return $dataTable;
+ return Piwik_DataTable::makeFromIndexedArray($data);
}
}
diff --git a/plugins/SEO/templates/index.twig b/plugins/SEO/templates/index.twig
index 1d6e9deb22..0a147b4f62 100644
--- a/plugins/SEO/templates/index.twig
+++ b/plugins/SEO/templates/index.twig
@@ -1,5 +1,5 @@
<div id='SeoRanks'>
- <script type="text/javascript" src="plugins/SEO/javascripts/rank.js"></script>
+ <script type="text/javascript" src="plugins/SEO/templates/rank.js"></script>
<form method="post" style="padding: 8px;">
<div align="left" class="mediumtext">
@@ -10,8 +10,8 @@
</span>
</div>
- {% import 'ajaxMacros.twig' as ajax %}
- {{ ajax.loadingDiv('ajaxLoadingSEO') }}
+ {% import "ajaxMacros.twig" as ajax %}
+ {{ ajax.LoadingDiv('ajaxLoadingSEO') }}
<div id="rankStats" align="left" style='margin-top:10px'>
{% if ranks is empty %}
@@ -24,27 +24,26 @@
<table cellspacing='2' style='margin:auto;line-height:1.5em;padding-top:10px'>
{% for rank in ranks %}
<tr>
- {% set seoLink %}<a class="linkContent" href="?module=Proxy&action=redirect&url={{ rank.logo_link|url_encode }}" target="_blank"
- {% if rank.logo_tooltip is not empty %}title="{{ rank.logo_tooltip }}"{% endif %}>
- {% endset %}
+{% set seoLink %}{% if rank.logo_link is not empty %}<a class="linkContent" href="?module=Proxy&action=redirect&url={{ rank.logo_link|url_encode }}" target="_blank"
+ {% if rank.logo_tooltip is not empty %}title="{{ rank.logo_tooltip }}"{% endif %}>{% endif %}{% endset %}
{% set majesticLink %}{{ seoLink }}Majestic</a>{% endset %}
- <td>
- {% if rank.logo_link is not empty %}{{ seoLink }}{% endif %}
- <img style='vertical-align:middle;margin-right:6px;' src='{{ rank.logo }}' border='0'
- alt="{{ rank.label }}">{% if rank.logo_link is not empty %}</a>{% endif %} {{ rank.label|replace({"Majestic":majesticLink}) }}
+ <td>{% if rank.logo_link is not empty %}{{ seoLink }}{% endif %}<img
+ style='vertical-align:middle;margin-right:6px;' src='{{ rank.logo }}' border='0'
+ alt="{{ rank.label }}">{% if rank.logo_link is not empty %}</a>{% endif %} {{ rank.label|replace({"Majestic": majesticLink}) }}
</td>
<td>
<div style='margin-left:15px'>
- {% if rank.logo_link is not empty %}{{ seoLink }}{% endif %}
- {% if rank.rank is defined %}{{ rank.rank }}{% else %}-{% endif %}
- {% if rank.id=='pagerank' %} /10
- {% elseif rank.id=='google-index' or rank.id=='bing-index' %} {{ 'SEO_Pages'|translate }}
- {% endif %}
+ {% if rank.logo_link is not empty }{{ seoLink }}{% endif %}
+ {% if rank.rank %}{{ rank.rank }}{% else% }-{% endif %}
+ {% if rank.id=='pagerank' %} /10
+ {% elseif rank.id=='google-index' or rank.id=='bing-index' %} {{ 'SEO_Pages'|translate }}
+ {% endif %}
{% if rank.logo_link is not empty %}</a>{% endif %}
</div>
</td>
</tr>
{% endfor %}
+
</table>
{% endif %}
</div>
diff --git a/plugins/SegmentEditor/API.php b/plugins/SegmentEditor/API.php
index 19083f8e42..ad420009ea 100644
--- a/plugins/SegmentEditor/API.php
+++ b/plugins/SegmentEditor/API.php
@@ -10,12 +10,14 @@
*/
/**
- * The SegmentEditor API lets you add, update, delete custom Segments, and list saved segments.
+ * The SegmentEditor API lets you add, update, delete custom Segments, and list saved segments.a
*
* @package Piwik_SegmentEditor
*/
class Piwik_SegmentEditor_API
{
+ const DELETE_SEGMENT_EVENT = 'SegmentEditor.delete';
+
static private $instance = null;
/**
@@ -31,12 +33,19 @@ class Piwik_SegmentEditor_API
protected function checkSegmentValue($definition, $idSite)
{
+ // unsanitize so we don't record the HTML entitied segment
+ $definition = Piwik_Common::unsanitizeInputValue($definition);
+ $definition = str_replace("#", '%23', $definition); // hash delimiter
+ $definition = str_replace("'", '%27', $definition); // not encoded in JS
+ $definition = str_replace("&", '%26', $definition);
+
try {
$segment = new Piwik_Segment($definition, $idSite);
$segment->getHash();
} catch (Exception $e) {
throw new Exception("The specified segment is invalid: " . $e->getMessage());
}
+ return $definition;
}
protected function checkSegmentName($name)
@@ -52,16 +61,11 @@ class Piwik_SegmentEditor_API
if ($enabledAllUsers
&& !Piwik::isUserIsSuperUser()
) {
- throw new Exception("&enabledAllUsers=1 requires Super User access");
+ throw new Exception("enabledAllUsers=1 requires Super User access");
}
return $enabledAllUsers;
}
-
- /**
- * @param $idSite
- * @throws Exception
- */
protected function checkIdSite($idSite)
{
if (empty($idSite)) {
@@ -74,6 +78,8 @@ class Piwik_SegmentEditor_API
}
Piwik::checkUserHasViewAccess($idSite);
}
+ $idSite = (int)$idSite;
+ return $idSite;
}
protected function checkAutoArchive($autoArchive, $idSite)
@@ -94,23 +100,63 @@ class Piwik_SegmentEditor_API
return $autoArchive;
}
+ protected function getSegmentOrFail($idSegment)
+ {
+ $segment = $this->get($idSegment);
+
+ if (empty($segment)) {
+ throw new Exception("Requested segment not found");
+ }
+ return $segment;
+ }
+
+ protected function checkUserIsNotAnonymous()
+ {
+ if(Piwik::isUserIsAnonymous()) {
+ throw new Exception("To create, edit or delete Custom Segments, please sign in first.");
+ }
+ }
+
+ /**
+ * Deletes a stored segment.
+ *
+ * @param $idSegment
+ */
public function delete($idSegment)
{
+ $this->checkUserIsNotAnonymous();
+
+ // allow plugins using the segment to throw an exception or propagate the deletion
+ Piwik_PostEvent(self::DELETE_SEGMENT_EVENT, $idSegment);
+
$segment = $this->getSegmentOrFail($idSegment);
$db = Zend_Registry::get('db');
$db->delete(Piwik_Common::prefixTable('segment'), 'idsegment = ' . $idSegment);
return true;
}
+ /**
+ * Modifies an existing stored segment.
+ *
+ * @param $idSegment The ID of the stored segment to modify.
+ * @param $name The new name of the segment.
+ * @param $definition The new definition of the segment.
+ * @param bool $idSite If supplied, associates the stored segment with as single site.
+ * @param bool $autoArchive Whether to automatically archive data with the segment or not.
+ * @param bool $enabledAllUsers Whether the stored segment is viewable by all users or just the one that created it.
+ *
+ */
public function update($idSegment, $name, $definition, $idSite = false, $autoArchive = false, $enabledAllUsers = false)
{
- $this->checkIdSite($idSite);
+ $this->checkUserIsNotAnonymous();
+ $segment = $this->getSegmentOrFail($idSegment);
+
+ $idSite = $this->checkIdSite($idSite);
$this->checkSegmentName($name);
- $this->checkSegmentValue($definition, $idSite);
+ $definition = $this->checkSegmentValue($definition, $idSite);
$enabledAllUsers = $this->checkEnabledAllUsers($enabledAllUsers);
$autoArchive = $this->checkAutoArchive($autoArchive, $idSite);
- $segment = $this->getSegmentOrFail($idSegment);
$bind = array(
'name' => $name,
'definition' => $definition,
@@ -128,13 +174,23 @@ class Piwik_SegmentEditor_API
return true;
}
-
+ /**
+ * Adds a new stored segment.
+ *
+ * @param $name The new name of the segment.
+ * @param $definition The new definition of the segment.
+ * @param bool $idSite If supplied, associates the stored segment with as single site.
+ * @param bool $autoArchive Whether to automatically archive data with the segment or not.
+ * @param bool $enabledAllUsers Whether the stored segment is viewable by all users or just the one that created it.
+ *
+ * @return int The newly created segment Id
+ */
public function add($name, $definition, $idSite = false, $autoArchive = false, $enabledAllUsers = false)
{
- Piwik::checkUserIsNotAnonymous();
- $this->checkIdSite($idSite);
+ $this->checkUserIsNotAnonymous();
+ $idSite = $this->checkIdSite($idSite);
$this->checkSegmentName($name);
- $this->checkSegmentValue($definition, $idSite);
+ $definition = $this->checkSegmentValue($definition, $idSite);
$enabledAllUsers = $this->checkEnabledAllUsers($enabledAllUsers);
$autoArchive = $this->checkAutoArchive($autoArchive, $idSite);
@@ -153,25 +209,11 @@ class Piwik_SegmentEditor_API
return $db->lastInsertId();
}
- public function getSegmentsToAutoArchive($idSite = false)
- {
- Piwik::checkUserIsSuperUser();
-
- $sqlRestrictSite = '';
- $bind = array();
- if ($idSite) {
- $sqlRestrictSite = 'OR enable_only_idsite = ?';
- $bind = array($idSite);
- }
- $segments = Zend_Registry::get('db')->fetchAll("SELECT *
- FROM " . Piwik_Common::prefixTable("segment") . "
- WHERE auto_archive = 1
- AND deleted = 0
- AND (enable_only_idsite IS NULL " . $sqlRestrictSite . " )", $bind
- );
- return $segments;
- }
-
+ /**
+ * Returns a stored segment by ID
+ *
+ * @param $idSegment
+ */
public function get($idSegment)
{
Piwik::checkUserHasSomeViewAccess();
@@ -179,8 +221,8 @@ class Piwik_SegmentEditor_API
throw new Exception("idSegment should be numeric.");
}
$segment = Zend_Registry::get('db')->fetchRow("SELECT * " .
- " FROM " . Piwik_Common::prefixTable("segment") .
- " WHERE idsegment = ?", $idSegment);
+ " FROM " . Piwik_Common::prefixTable("segment") .
+ " WHERE idsegment = ?", $idSegment);
if (empty($segment)) {
return false;
@@ -188,40 +230,54 @@ class Piwik_SegmentEditor_API
try {
Piwik::checkUserIsSuperUserOrTheUser($segment['login']);
} catch (Exception $e) {
- throw new Exception("You can only manage your own segments (unless you are Super User).");
+ throw new Exception("You can only edit the custom segments you have created yourself. This segment was created and 'shared with you' by the Super User. " .
+ "To modify this segment, you can first create a new one by clicking on 'Add new segment'. Then you can customize the segment's definition.");
}
if ($segment['deleted']) {
- throw new Exception("This segment is marked as deleted.");
+ throw new Exception("This segment is marked as deleted. ");
}
return $segment;
}
/**
- * @param $idSegment
- * @throws Exception
+ * Returns all stored segments.
+ *
+ * @param bool $idSite Whether to return stored segments that are only auto-archived for a specific idSite, or all of them. If supplied, must be a valid site ID.
+ * @param bool $returnOnlyAutoArchived Whether to only return stored segments that are auto-archived or not.
+ * @return array
*/
- protected function getSegmentOrFail($idSegment)
+ public function getAll($idSite = false, $returnOnlyAutoArchived = false)
{
- $segment = $this->get($idSegment);
+ if(!empty($idSite) ) {
+ Piwik::checkUserHasViewAccess($idSite);
+ } else {
+ Piwik::checkUserHasSomeViewAccess();
+ }
+ $bind = array();
- if (empty($segment)) {
- throw new Exception("Requested segment not found");
+ // Build basic segment filtering
+ $whereIdSite = '';
+ if(!empty($idSite)) {
+ $whereIdSite = 'enable_only_idsite = ? OR ';
+ $bind[] = $idSite;
}
- return $segment;
- }
- public function getAll($idSite)
- {
- Piwik::checkUserHasViewAccess($idSite);
+ $bind[] = Piwik::getCurrentUserLogin();
+
+ $extraWhere = '';
+ if($returnOnlyAutoArchived) {
+ $extraWhere = ' AND auto_archive = 1';
+ }
+ // Query
$sql = "SELECT * " .
- " FROM " . Piwik_Common::prefixTable("segment") .
- " WHERE (enable_only_idsite = ? OR enable_only_idsite IS NULL)
- AND (enable_all_users = 1 OR login = ?)
- AND deleted = 0
- ORDER BY name ASC";
- $bind = array($idSite, Piwik::getCurrentUserLogin());
+ " FROM " . Piwik_Common::prefixTable("segment") .
+ " WHERE ($whereIdSite enable_only_idsite = 0)
+ AND (enable_all_users = 1 OR login = ?)
+ AND deleted = 0
+ $extraWhere
+ ORDER BY name ASC";
$segments = Zend_Registry::get('db')->fetchAll($sql, $bind);
return $segments;
diff --git a/plugins/SegmentEditor/Controller.php b/plugins/SegmentEditor/Controller.php
index aeb69978f8..7c96058ddb 100644
--- a/plugins/SegmentEditor/Controller.php
+++ b/plugins/SegmentEditor/Controller.php
@@ -24,29 +24,67 @@ class Piwik_SegmentEditor_Controller extends Piwik_Controller
$segmentsByCategory = $customVariablesSegments = array();
foreach($segments as $segment) {
- if($segment['category'] == 'Visit'
+ if($segment['category'] == Piwik_Translate('General_Visit')
&& $segment['type'] == 'metric') {
- $segment['category'] .= ' (' . lcfirst(Piwik_Translate('General_Metrics')) . ')';
+ $metricsLabel = Piwik_Translate('General_Metrics');
+ $metricsLabel[0] = strtolower($metricsLabel[0]);
+ $segment['category'] .= ' (' . $metricsLabel . ')';
}
$segmentsByCategory[$segment['category']][] = $segment;
}
- uksort($segmentsByCategory, array($this, 'sortCustomVariablesLast'));
+ uksort($segmentsByCategory, array($this, 'sortSegmentCategories'));
$view->segmentsByCategory = $segmentsByCategory;
-
$savedSegments = Piwik_SegmentEditor_API::getInstance()->getAll($idSite);
+ foreach($savedSegments as &$savedSegment) {
+ $savedSegment['name'] = Piwik_Common::sanitizeInputValue($savedSegment['name']);
+ }
$view->savedSegmentsJson = Piwik_Common::json_encode($savedSegments);
+ $view->authorizedToCreateSegments = !Piwik::isUserIsAnonymous();
+ $view->segmentTranslations = Piwik_Common::json_encode($this->getTranslations());
$out = $view->render();
echo $out;
}
- public function sortCustomVariablesLast($a, $b)
+ public function sortSegmentCategories($a, $b)
{
+ // Custom Variables last
if($a == Piwik_Translate('CustomVariables_CustomVariables')) {
return 1;
}
- return -1;
+ return 0;
+ }
+
+ private function getTranslations()
+ {
+ $translationKeys = array(
+ 'General_OperationEquals',
+ 'General_OperationNotEquals',
+ 'General_OperationAtMost',
+ 'General_OperationAtLeast',
+ 'General_OperationLessThan',
+ 'General_OperationGreaterThan',
+ 'General_OperationContains',
+ 'General_OperationDoesNotContain',
+ 'General_OperationIs',
+ 'General_OperationIsNot',
+ 'General_OperationContains',
+ 'General_OperationDoesNotContain',
+ 'SegmentEditor_DefaultAllVisits',
+ 'General_DefaultAppended',
+ 'SegmentEditor_AddNewSegment',
+ 'General_Edit',
+ 'General_Search',
+ 'General_SearchNoResults',
+ '',
+ '',
+ '',
+ );
+ foreach($translationKeys as $key) {
+ $translations[$key] = Piwik_Translate($key);
+ }
+ return $translations;
}
}
diff --git a/plugins/SegmentEditor/SegmentEditor.php b/plugins/SegmentEditor/SegmentEditor.php
index 7615873a95..d95f9b8a8c 100644
--- a/plugins/SegmentEditor/SegmentEditor.php
+++ b/plugins/SegmentEditor/SegmentEditor.php
@@ -45,9 +45,9 @@ class Piwik_SegmentEditor extends Piwik_Plugin
public function getKnownSegmentsToArchiveAllSites($notification)
{
$segments =& $notification->getNotificationObject();
- $segmentToAutoArchive = Piwik_SegmentEditor_API::getInstance()->getSegmentsToAutoArchive();
- if (!empty($segmentToAutoArchive)) {
- $segments = array_merge($segments, $segmentToAutoArchive);
+ $segmentsToAutoArchive = Piwik_SegmentEditor_API::getInstance()->getAll($idSite = false, $returnAutoArchived = true);
+ foreach ($segmentsToAutoArchive as $segment) {
+ $segments[] = $segment['definition'];
}
}
@@ -55,7 +55,7 @@ class Piwik_SegmentEditor extends Piwik_Plugin
{
$segments =& $notification->getNotificationObject();
$idSite = $notification->getNotificationInfo();
- $segmentToAutoArchive = Piwik_SegmentEditor_API::getInstance()->getSegmentsToAutoArchive($idSite);
+ $segmentToAutoArchive = Piwik_SegmentEditor_API::getInstance()->getAll($idSite, $returnAutoArchived = true);
foreach ($segmentToAutoArchive as $segmentInfo) {
$segments[] = $segmentInfo['definition'];
diff --git a/plugins/SegmentEditor/images/dashboard_h_bg_hover.png b/plugins/SegmentEditor/images/dashboard_h_bg_hover.png
index 37e4bd54e8..e46d8cc6df 100644
--- a/plugins/SegmentEditor/images/dashboard_h_bg_hover.png
+++ b/plugins/SegmentEditor/images/dashboard_h_bg_hover.png
Binary files differ
diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js
index d18dc9ee47..16bd22f5d4 100644
--- a/plugins/SegmentEditor/javascripts/Segmentation.js
+++ b/plugins/SegmentEditor/javascripts/Segmentation.js
@@ -10,12 +10,12 @@ Segmentation = (function($) {
var segmentation = function segmentation(config) {
var self = this;
- // set defaults for widget
+
self.currentSegmentStr = "";
self.targetId = "segmentEditorPanel";
self.segmentAccess = "read";
- self.segmentList = [];
- // -----------
+ self.availableSegments = [];
+
for(var item in config)
{
self[item] = config[item];
@@ -23,35 +23,40 @@ Segmentation = (function($) {
self.timer = ""; // variable for further use in timing events
self.searchAllowed = true;
- //----------
self.availableMatches = [];
self.availableMatches["metric"] = [];
- self.availableMatches["metric"]["=="] = "Equals";
- self.availableMatches["metric"]["!="] = "Not Equals";
- self.availableMatches["metric"]["<="] = "At most";
- self.availableMatches["metric"][">="] = "At least";
- self.availableMatches["metric"]["<"] = "Less than";
- self.availableMatches["metric"][">"] = "Greater than";
+ self.availableMatches["metric"]["=="] = self.translations['General_OperationEquals'];
+ self.availableMatches["metric"]["!="] = self.translations['General_OperationNotEquals'];
+ self.availableMatches["metric"]["<="] = self.translations['General_OperationAtMost'];
+ self.availableMatches["metric"][">="] = self.translations['General_OperationAtLeast'];
+ self.availableMatches["metric"]["<"] = self.translations['General_OperationLessThan'];
+ self.availableMatches["metric"][">"] = self.translations['General_OperationGreaterThan'];
self.availableMatches["dimension"] = [];
- self.availableMatches["dimension"]["=="] = "Is";
- self.availableMatches["dimension"]["!="] = "Is not";
- self.availableMatches["dimension"]["=@"] = "Contains";
- self.availableMatches["dimension"]["!@"] = "Does not contain";
+ self.availableMatches["dimension"]["=="] = self.translations['General_OperationIs'];
+ self.availableMatches["dimension"]["!="] = self.translations['General_OperationIsNot'];
+ self.availableMatches["dimension"]["=@"] = self.translations['General_OperationContains'];
+ self.availableMatches["dimension"]["!@"] = self.translations['General_OperationDoesNotContain'];
segmentation.prototype.getSegment = function(){
var self = this;
- return self.currentSegmentStr;
+ if($.browser.mozilla) {
+ return self.currentSegmentStr;
+ }
+ return decodeURIComponent(self.currentSegmentStr);
}
var setSegment = function(segmentStr){
+ if(!$.browser.mozilla) {
+ segmentStr = encodeURIComponent(segmentStr);
+ }
self.currentSegmentStr = segmentStr;
}
segmentation.prototype.shortenSegmentName = function(name, length){
- if(typeof length === "undefined") length = 30;
+ if(typeof length === "undefined") length = 26;
if(typeof name === "undefined") name = "";
var i;
@@ -73,24 +78,22 @@ Segmentation = (function($) {
var markCurrentSegment = function(){
var current = self.getSegment();
-// window.setTimeout(function(){
- var segmentationTitle = $(self.content).find(".segmentationTitle");
- if( current != "")
- {
- var foundItems = $(self.content).find('div.segmentList > ul > li[data-definition="'+current+'"]');
- if( foundItems.length > 0)
- {
- var name = $(foundItems).first().find("span.segname").text();
- segmentationTitle.html("<b>"+name+"</b>");
- }
- else{
- segmentationTitle.html("<b>Custom Segment</b>");
- }
- }
- else {
- $(self.content).find(".segmentationTitle").text("All visits");
+
+ var segmentationTitle = $(self.content).find(".segmentationTitle");
+ if( current != "")
+ {
+ var selector = 'div.segmentList ul li[data-definition="'+current+'"]';
+ var foundItems = $(selector);
+ if( foundItems.length > 0) {
+ var name = $(foundItems).first().find("span.segname").text();
+ segmentationTitle.html("<b>"+name+"</b>");
+ } else {
+ segmentationTitle.html("<b>Custom Segment</b>");
}
-// }, 20);
+ }
+ else {
+ $(self.content).find(".segmentationTitle").text(self.translations['SegmentEditor_DefaultAllVisits']);
+ }
}
var getAndDiv = function(){
@@ -179,35 +182,50 @@ Segmentation = (function($) {
$(self.form).find(".segment-content").append(getInitialStateRowsHtml());
doDragDropBindings();
}
-
+
+ var getSegmentFromId = function (id) {
+ if(self.availableSegments.length > 0) {
+ for(var i = 0; i < self.availableSegments.length; i++)
+ {
+ segment = self.availableSegments[i];
+ if(segment.idsegment == id) {
+ return segment;
+ }
+ }
+ }
+ return false;
+ }
+
var getListHtml = function() {
var html = $("#SegmentEditor > .listHtml").clone();
var segment, injClass;
var listHtml = '<li data-idsegment="" ' +
- (self.currentSegmentsGlobal == "" ? " class='segmentSelected' " : "")
- + ' data-definition=""><span class="segname">All Visits (default)</span></li> ';
- if(self.segmentList.length > 0) {
- for(var key in self.segmentList)
+ (self.currentSegmentStr == "" ? " class='segmentSelected' " : "")
+ + ' data-definition=""><span class="segname">' + self.translations['SegmentEditor_DefaultAllVisits']
+ + ' ' + self.translations['General_DefaultAppended']
+ + '</span></li> ';
+ if(self.availableSegments.length > 0) {
+ for(var i = 0; i < self.availableSegments.length; i++)
{
- segment = self.segmentList[key];
+ segment = self.availableSegments[i];
injClass = "";
- if( segment.definition == self.currentSegmentsGlobal){
+ if( segment.definition == self.currentSegmentStr){
injClass = 'class="segmentSelected"';
}
- listHtml += '<li data-idsegment="'+segment.idsegment+'" data-definition=\''+segment.definition+'\' '
+ listHtml += '<li data-idsegment="'+segment.idsegment+'" data-definition="'+ (segment.definition).replace(/"/g, '&quot;') +'" '
+ injClass +' title="'+segment.name+'"><span class="segname">'
+ self.shortenSegmentName(segment.name)+'</span>';
if(self.segmentAccess == "write") {
- listHtml += '<span class="editSegment">[edit]</span>';
+ listHtml += '<span class="editSegment">['+ self.translations['General_Edit'].toLocaleLowerCase() +']</span>';
}
listHtml += '</li>';
}
$(html).find(".segmentList > ul").append(listHtml);
if(self.segmentAccess === "write"){
- $(html).find(".add_new_segment").html(_pk_translate('General_AddNewSegment_js'));
+ $(html).find(".add_new_segment").html(self.translations['SegmentEditor_AddNewSegment']);
}
- else{
+ else {
$(html).find(".add_new_segment").hide();;
}
}
@@ -224,12 +242,15 @@ Segmentation = (function($) {
//$("body").append(html);
var segmentsDropdown = $(html).find("#available_segments_select");
var segment, newOption;
- newOption = '<option data-idsegment="" data-definition="" >New segment</option>';
+ newOption = '<option data-idsegment="" data-definition="" title="'
+ + self.translations['SegmentEditor_AddNewSegment']
+ + '">' + self.translations['SegmentEditor_AddNewSegment']
+ + '</option>';
segmentsDropdown.append(newOption);
- for(var key in self.segmentList)
+ for(var i = 0; i < self.availableSegments.length; i++)
{
- segment = self.segmentList[key];
- newOption = '<option data-idsegment="'+segment.idsegment+'" data-definition=\''+segment.definition+'\' title="'+segment.name+'">'+self.shortenSegmentName(segment.name)+'</option>';
+ segment = self.availableSegments[i];
+ newOption = '<option data-idsegment="'+segment.idsegment+'" data-definition="'+(segment.definition).replace(/"/g, '&quot;')+'" title="'+segment.name+'">'+self.shortenSegmentName(segment.name)+'</option>';
segmentsDropdown.append(newOption);
}
$(html).find(".segment-content > h3").after(getInitialStateRowsHtml()).show();
@@ -274,7 +295,7 @@ Segmentation = (function($) {
if( index != -1){
if(index < minPos){
minPos = index;
- if(match == ">" || match == "<"){
+ if(match.length == 1){
singleChar = true;
}
}
@@ -287,13 +308,10 @@ Segmentation = (function($) {
newMetric.metric = metric.substr(0,minPos);
newMetric.match = metric.substr(minPos,1);
newMetric.value = metric.substr(minPos+1);
-
- }
- else{
+ } else {
newMetric.metric = metric.substr(0,minPos);
newMetric.match = metric.substr(minPos,2);
newMetric.value = metric.substr(minPos+2);
-
}
// if value is only "" -> change to empty string
if(newMetric.value == '""')
@@ -301,6 +319,8 @@ Segmentation = (function($) {
newMetric.value = "";
}
}
+
+ newMetric.value = decodeURIComponent(newMetric.value);
return newMetric;
}
@@ -318,11 +338,15 @@ Segmentation = (function($) {
}
var openEditForm = function(segment){
- addForm();
+ addForm("edit", segment);
+
$(self.form).find(".segment-content > h3 > span").text(segment.name);
$(self.form).find('#available_segments_select > option[data-idsegment="'+segment.idsegment+'"]').prop("selected",true);
+
$(self.form).find('#available_segments a.dropList').html(self.shortenSegmentName(segment.name, 16));
-
+
+
+
if(segment.definition != ""){
revokeInitialStateRows();
var blocks = parseSegmentStr(segment.definition);
@@ -361,11 +385,8 @@ Segmentation = (function($) {
$(self.content).off("click",".editSegment").on("click", ".editSegment", function(e){
$(this).parents(".segmentationContainer").trigger("click");
var target = $(this).parent("li");
- var segment = {};
- segment.idsegment = target.attr("data-idsegment");
- segment.definition = target.attr("data-definition");
- segment.name = target.attr("title");
- openEditForm(segment);
+
+ openEditFormGivenSegment(target);
e.stopPropagation();
e.preventDefault();
});
@@ -374,9 +395,11 @@ Segmentation = (function($) {
if($(e.currentTarget).hasClass("grayed") !== true){
var segment = {};
segment.idsegment = $(this).attr("data-idsegment");
- segment.definition = $(this).attr("data-definition");
+ segment.definition = $(this).data("definition");
segment.name = $(this).attr("title");
- self.segmentSelectMethod(segment);
+
+ self.segmentSelectMethod( segment.definition );
+ toggleLoadingMessage( segment.definition.length );
setSegment(segment.definition);
markCurrentSegment();
}
@@ -390,11 +413,14 @@ Segmentation = (function($) {
persist = false;
}
alterMatchesList(this, persist);
+
doDragDropBindings();
+
autoSuggestValues(this, persist);
} );
}
+ // Request auto-suggest values
var autoSuggestValues = function(select, persist) {
var type = $(select).find("option:selected").attr("value");
if(!persist) {
@@ -404,14 +430,12 @@ Segmentation = (function($) {
var inputElement = parents.find(".metricValueBlock input");
var segmentName = $('option:selected',select).attr('value');
- // Request auto-suggest values
var ajaxHandler = new ajaxHelper();
ajaxHandler.addParams({
module: 'API',
format: 'json',
method: 'API.getSuggestedValuesForSegment',
- segmentName: segmentName,
- idSite: piwik.idSite
+ segmentName: segmentName
}, 'GET');
ajaxHandler.setCallback(function(response) {
loadingElement.hide();
@@ -425,9 +449,11 @@ Segmentation = (function($) {
}
});
- inputElement.click(function(e){ inputElement.keydown() });
+ inputElement.click(function(e){
+ inputElement.autocomplete('search', $(inputElement).val());
+ });
});
- ajaxHandler.send(true);
+ ajaxHandler.send();
}
}
@@ -437,8 +463,7 @@ Segmentation = (function($) {
var matchSelector = $(select).parents(".segment-input").siblings(".metricMatchBlock").find("select");
if(persist === true){
oldMatch = matchSelector.find("option:selected").val();
- }
- else{
+ } else {
oldMatch = "";
}
@@ -482,9 +507,27 @@ Segmentation = (function($) {
});
}
+ function openEditFormGivenSegment(option) {
+ var segment = {};
+ segment.idsegment = option.attr("data-idsegment");
+
+
+ var segmentExtra = getSegmentFromId(segment.idsegment);
+ for(var item in segmentExtra)
+ {
+ segment[item] = segmentExtra[item];
+ }
+
+ segment.name = option.attr("title");
+
+ segment.definition = option.data("definition");
+
+ openEditForm(segment);
+ }
+
var bindFormEvents = function(){
- $(self.form).on("click", "a", function(e){
+ $(self.form).on("click", "a:not(.crowdfundingLink)", function(e){
e.preventDefault();
});
@@ -501,11 +544,18 @@ Segmentation = (function($) {
$(e.currentTarget).siblings("#edit_segment_name").focus().val(oldName);
});
+
+ $(self.form).off("click", ".segmentName").on("click", ".segmentName", function(e) {
+ $(self.form).find("a.editSegmentName").trigger('click');
+ });
+
$(self.form).off("blur", "input#edit_segment_name").on("blur", "input#edit_segment_name", function(e){
var newName = $(this).val();
- $(e.currentTarget).parents("h3").find("span").text(newName).show();
- $(self.form).find("a.editSegmentName").show();
- $(this).remove();
+ if(newName.trim() != '') {
+ $(e.currentTarget).parents("h3").find("span").text(newName).show();
+ $(self.form).find("a.editSegmentName").show();
+ $(this).remove();
+ }
});
$(self.form).on("click", '.segment-element', function(event) {
@@ -515,12 +565,7 @@ Segmentation = (function($) {
$(self.form).find("#available_segments_select").bind("change", function(e){
var option = $(e.currentTarget).find('option:selected');
- var segment = {};
- segment.idsegment = option.attr("data-idsegment");
- segment.name = option.attr("title");
- segment.definition = option.attr("data-definition");
- openEditForm(segment);
-
+ openEditFormGivenSegment(option);
});
// attach event that shows/hides child elements of each metric category
@@ -531,23 +576,22 @@ Segmentation = (function($) {
});
$(self.form).off("click", ".custom_select_search a").on("click", ".custom_select_search a", function(e){
- $(self.form).find("#segmentSearch").val("").trigger("keyup").val("Search");
+ $(self.form).find("#segmentSearch").val("").trigger("keyup").val(self.translations['General_Search']);
});
// attach event that will clear search input upon focus if its content is default
$(self.form).find("#segmentSearch").on("focus", function(e){
var search = $(e.currentTarget).val();
- if(search == "Search")
+ if(search == self.translations['General_Search'])
$(e.currentTarget).val("");
});
-
// attach event that will set search input value upon blur if its content is not null
$(self.form).find("#segmentSearch").on("blur", function(e){
var search = $(e.currentTarget).val();
if(search == ""){
clearSearchMetricHighlight();
- $(e.currentTarget).val("Search");
+ $(e.currentTarget).val(self.translations['General_Search']);
}
});
@@ -568,17 +612,15 @@ Segmentation = (function($) {
}
});
-
$(self.form).on("click", ".delete", function(){
-
var segmentName = $(self.form).find(".segment-content > h3 > span").text();
var segmentId = $(self.form).find("#available_segments_select option:selected").attr("data-idsegment")
var params = {
"idsegment" : segmentId
};
- $('#confirm').find('#name').text( segmentName );
+ $('#segment-delete-confirm').find('#name').text( segmentName );
if(segmentId != ""){
- piwikHelper.modalConfirm( '#confirm', {
+ piwikHelper.modalConfirm( '#segment-delete-confirm', {
yes: function(){
self.deleteMethod(params);
}
@@ -601,7 +643,6 @@ Segmentation = (function($) {
bindChangeMetricSelectEvent();
placeSegmentationFormControls();
-
}
var doDragDropBindings = function(){
@@ -643,27 +684,30 @@ Segmentation = (function($) {
}
var searchSegments = function(search){
- // pre-proces search string to normalized form
+ // pre-process search string to normalized form
search = normalizeSearchString(search);
+
// ---
// clear all previous search highlights and hide all categories
// to allow further showing only matching ones, while others remain invisible
clearSearchMetricHighlight();
$(self.form).find('.segment-nav div > ul > li').hide();
var curStr = "";
+ var curMetric = "";
+
// 1 - do most obvious selection -> mark whole categories matching search string
// also expand whole category
$(self.form).find('.segment-nav div > ul > li').each( function(){
- curStr = normalizeSearchString($(this).text())
- if(curStr.indexOf(search) > -1){
- $(this).addClass("searchFound");
- $(this).find("ul").show();
- $(this).find("li").show();
- $(this).show();
+ curStr = normalizeSearchString($(this).find("a.metric_category").text())
+ if(curStr.indexOf(search) > -1) {
+ $(this).addClass("searchFound");
+ $(this).find("ul").show();
+ $(this).find("li").show();
+ $(this).show();
+ }
}
- });
+ );
- var curMetric = "";
// 2 - among all unselected categories find metrics which match and mark parent as search result
$(self.form).find(".segment-nav div > ul > li:not(.searchFound)").each(function(){
var parent = this;
@@ -677,19 +721,11 @@ Segmentation = (function($) {
$(parent).addClass("searchFound").show();
}
});
-
- // if(curStr.indexOf(search) > -1 || curMetric.indexOf(search) > -1)
- // {
- // $(this).addClass("searchFound");
- // $(this).find('li').hide();
- // $(this).find('li[data*="'+search+'"]').show();
- // }
});
if( $(self.form).find("li.searchFound").length == 0)
{
- $(self.form).find("div > ul").prepend('<li class="no_results"><a>No results</a></li>').show();
-
+ $(self.form).find("div > ul").prepend('<li class="no_results"><a>'+self.translations['General_SearchNoResults']+'</a></li>').show();
}
// check if search allow flag was revoked - then clear all search results
if(self.searchAllowed == false)
@@ -743,7 +779,7 @@ Segmentation = (function($) {
}
});
- // add new OR block
+ // add new OR block
$(self.form).on("click", ".segment-add-or a", function(event, data){
$(event.currentTarget).parents(".segment-rows").find(".segment-or:last").after(getOrDiv()).after(getMockedInputRowHtml());
if(typeof data !== "undefined"){
@@ -771,7 +807,8 @@ Segmentation = (function($) {
});
}
- var addForm = function(mode){
+ // Mode = 'new' or 'edit'
+ var addForm = function(mode, segment){
$("#segmentEditorPanel").find(".segment-element:visible").unbind().remove();
if(typeof self.form !== "undefined")
@@ -786,18 +823,32 @@ Segmentation = (function($) {
setLeftMargin('#segmentEditorPanel > .segment-element');
bindFormEvents();
bindSegmentManipulationEvents();
- makeDropList("#enabledAllUsers" , "#enabledAllUsers_select");
+
+ if(mode == "edit") {
+ $(self.form).find('#enable_all_users_select > option[value="'+segment.enable_all_users+'"]').prop("selected",true);
+ $(self.form).find('#visible_to_website_select > option[value="'+segment.enable_only_idsite+'"]').prop("selected",true);
+ $(self.form).find('#auto_archive_select > option[value="'+segment.auto_archive+'"]').prop("selected",true);
+
+ }
+
+ makeDropList("#enable_all_users" , "#enable_all_users_select");
makeDropList("#visible_to_website" , "#visible_to_website_select");
+ makeDropList("#auto_archive" , "#auto_archive_select");
makeDropList("#available_segments" , "#available_segments_select");
$(self.form).find(".saveAndApply").bind("click", function(e){
e.preventDefault();
parseFormAndSave();
});
+ $(self.form).find('.segment-footer').hover( function() {
+ $('.segmentFooterNote').fadeIn();
+ }, function() {
+ $('.segmentFooterNote').fadeOut();
+ });
+
if(typeof mode !== "undefined" && mode == "new")
{
$(self.form).find(".editSegmentName").trigger('click');
- $(self.form).find("#edit_segment_name").val("");
}
$("#segmentList").hide();
@@ -816,9 +867,6 @@ Segmentation = (function($) {
var metric = $(this).find(".metricList option:selected").val();
var match = $(this).find(".metricMatchBlock > select option:selected").val();
var value = $(this).find(".segment-input input").val();
- /*if(value == ""){
- value= '';
- }*/
subSegmentStr += metric + match + encodeURIComponent(value);
});
});
@@ -835,13 +883,16 @@ Segmentation = (function($) {
var segmentName = $(self.form).find(".segment-content > h3 >span").text();
var segmentStr = parseForm();
var segmentId = $(self.form).find('#available_segments_select > option:selected').attr("data-idsegment");
- var user = $(self.form).find("#enabledAllUsers_select option:selected").val();
+ var user = $(self.form).find("#enable_all_users_select option:selected").val();
+ var autoArchive = $(self.form).find("#auto_archive_select option:selected").val() || 0;
var params = {
"name": segmentName,
"definition": segmentStr,
"enabledAllUsers": user,
- "idSite": $('#visible_to_website option:selected').attr('value')
+ "autoArchive": autoArchive,
+ "idSite": $(self.form).find("#visible_to_website_select option:selected").val()
};
+
// determine if save or update should be performed
if(segmentId === ""){
self.addMethod(params);
@@ -876,10 +927,10 @@ Segmentation = (function($) {
select: function( event, ui ) {
event.preventDefault();
ui.item.option.selected = true;
- if(ui.item.value) {
- dropList.text(ui.item.label);
- $(self.form).find(selectId).trigger("change");
- }
+ // Mark original select>option
+ $('#SegmentEditor ' + spanId + ' option[value="' + ui.item.value + '"]').prop('selected', true);
+ dropList.text(ui.item.label);
+ $(self.form).find(selectId).trigger("change");
}
})
.click(function() {
@@ -904,9 +955,15 @@ Segmentation = (function($) {
}
var setLeftMargin = function(selector) {
-// setTimeout( function() {
- $(selector).css({left: Math.max($('#periodString')[0].offsetWidth) + 10});
-// }, 500);
+ $(selector).css({left: Math.max($('#periodString')[0].offsetWidth) + 10});
+ }
+
+ function toggleLoadingMessage(segmentIsSet) {
+ if (segmentIsSet) {
+ $('#ajaxLoading .loadingSegment').show();
+ } else {
+ $('#ajaxLoading .loadingSegment').hide();
+ }
}
var initHtml = function() {
@@ -926,6 +983,10 @@ Segmentation = (function($) {
// assign content to object attribute to make it easil accesible through all widget methods
bindListEvents();
markCurrentSegment();
+
+ // Loading message
+ var segmentIsSet = self.getSegment().length;
+ toggleLoadingMessage(segmentIsSet);
}
initHtml();
};
@@ -940,12 +1001,19 @@ $(document).ready( function(){
return;
}
- var changeSegment = function(params){
+ var changeSegment = function(segmentDefinition){
$('#segmentEditorPanel a.close').click();
-
- return broadcast.propagateNewPage('segment=' + params.definition, true, true);
+ segmentDefinition = cleanupSegmentDefinition(segmentDefinition);
+ segmentDefinition = encodeURIComponent(segmentDefinition);
+ return broadcast.propagateNewPage('segment=' + segmentDefinition, true);
};
+ var cleanupSegmentDefinition = function(definition) {
+ definition = definition.replace("'", "%29");
+ definition = definition.replace("&", "%26");
+ return definition;
+ }
+
var addSegment = function(params){
var ajaxHandler = new ajaxHelper();
ajaxHandler.setLoadingElement();
@@ -954,13 +1022,15 @@ $(document).ready( function(){
"format": 'json',
"method": 'SegmentEditor.add'
});
+ params.definition = cleanupSegmentDefinition(params.definition);
+
ajaxHandler.addParams(params, 'GET');
ajaxHandler.useCallbackInCaseOfError();
ajaxHandler.setCallback(function (response) {
if (response && response.result == 'error') {
alert(response.message);
} else {
- changeSegment(params);
+ changeSegment(params.definition);
}
});
ajaxHandler.send(true);
@@ -974,13 +1044,15 @@ $(document).ready( function(){
"format": 'json',
"method": 'SegmentEditor.update'
});
+ params.definition = cleanupSegmentDefinition(params.definition);
+
ajaxHandler.addParams(params, 'GET');
ajaxHandler.useCallbackInCaseOfError();
ajaxHandler.setCallback(function (response) {
if (response && response.result == 'error') {
alert(response.message);
} else {
- changeSegment(params);
+ changeSegment(params.definition);
}
});
ajaxHandler.send(true);
@@ -997,26 +1069,31 @@ $(document).ready( function(){
ajaxHandler.addParams({
idSegment: params.idsegment
}, 'POST');
- ajaxHandler.redirectOnSuccess();
+// ajaxHandler.redirectOnSuccess();
ajaxHandler.setLoadingElement();
+ ajaxHandler.useCallbackInCaseOfError();
+ ajaxHandler.setCallback(function (response) {
+ if (response && response.result == 'error') {
+ alert(response.message);
+ } else {
+ return broadcast.propagateNewPage('segment=');
+ }
+ });
+
ajaxHandler.send(true);
};
- var testSegment = function(segmentStr){
- console.log(segmentStr);
- }
-
+ var segmentFromHash = broadcast.getParamValue('segment', location.hash);
var segmentationFtw = new Segmentation({
"targetId" : "segmentList",
"segmentAccess" : "write",
- "segmentList" : availableSegments,
+ "availableSegments" : availableSegments,
"addMethod": addSegment,
"updateMethod": updateSegment,
"deleteMethod": deleteSegment,
"segmentSelectMethod": changeSegment,
- "testSegmentMethod": testSegment,
- "currentSegmentStr": broadcast.getValueFromHash('segment'),
- "currentSegmentsGlobal": broadcast.getValueFromHash('segment')
+ "currentSegmentStr": segmentFromHash,
+ "translations": segmentTranslations
});
$('body').on('mouseup',function(e){
@@ -1026,10 +1103,9 @@ $(document).ready( function(){
$("#segmentList").show();
}
- if($(e.target).parents('.segmentList').length === 0 && $(".segmentationContainer").hasClass("visible")){
+ if($(e.target).parents('#segmentList').length === 0 && $(".segmentationContainer").hasClass("visible")){
$(".segmentationContainer").trigger("click");
}
-
});
-}); \ No newline at end of file
+});
diff --git a/plugins/SegmentEditor/stylesheets/segmentation.css b/plugins/SegmentEditor/stylesheets/segmentation.css
index 184592fb28..4160090cd6 100644
--- a/plugins/SegmentEditor/stylesheets/segmentation.css
+++ b/plugins/SegmentEditor/stylesheets/segmentation.css
@@ -1,4 +1,22 @@
/* ADDITIONAL STYLES*/
+.youMustBeLoggedIn {
+ font-size:8pt;
+ font-style: italic;
+
+}
+.segment-footer .segmentFooterNote {
+ display:none;
+ float: left;
+ padding-top: 9px;
+}
+.segment-footer .segmentFooterNote, .segment-element .segment-footer .segmentFooterNote a {
+ font-size: 8pt;
+ color: #888172;
+}
+.segment-element .segment-footer .segmentFooterNote a {
+ padding:0; margin:0;
+ text-decoration:underline;
+}
.searchFound {
border: 0px solid red;
}
@@ -66,7 +84,7 @@ div.scrollable {
font: 11px Arial;
color: #454545;
width: 125px;
- padding: 3px 0px 3px 5px;
+ padding: 4px 0 3px 7px;
border: none;
background: none;
}
@@ -349,7 +367,7 @@ div.scrollable {
}
.segment-element .segment-footer button {
- width: 178px;
+ min-width: 178px;
height: 30px;
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDE3OCAzMCIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+PGxpbmVhckdyYWRpZW50IGlkPSJoYXQwIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjUwJSIgeTE9IjEwMCUiIHgyPSI1MCUiIHkyPSItMS40MjEwODU0NzE1MjAyZS0xNCUiPgo8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjODM3OTZiIiBzdG9wLW9wYWNpdHk9IjEiLz4KPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjYWJhMzkzIiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgPC9saW5lYXJHcmFkaWVudD4KCjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxNzgiIGhlaWdodD0iMzAiIGZpbGw9InVybCgjaGF0MCkiIC8+Cjwvc3ZnPg==);
background-image: -moz-linear-gradient(bottom, #83796b 0%, #aba393 100%);
@@ -371,7 +389,7 @@ div.scrollable {
z-index: 2;
background: #f7f7f7;
border: 1px solid #e4e5e4;
- padding: 5px 10px 6px 5px;
+ padding: 5px 10px 6px 10px;
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
@@ -387,7 +405,6 @@ div.scrollable {
}
.segmentationContainer .submenu {
font-size: 13px;
- font-weight: bold;
min-width: 180px;
}
.segmentationContainer .submenu ul {
@@ -402,13 +419,14 @@ div.scrollable {
padding-top: 10px;
}
.segmentationContainer .submenu ul li {
- padding: 2px 0px 1px 10px;
+ padding: 2px 0px 1px 6px;
margin: 3px 0 0 0;
cursor: pointer;
}
.segmentationContainer .submenu ul li:hover {
color: #255792;
margin: 0;
+ margin-left:-3px;
border: 1px solid #d5d2c6;
border-bottom: 2px solid #918f88;
border-radius: 4px;
@@ -446,6 +464,7 @@ div.scrollable {
margin: 12px 0 10px;
padding: 3px 10px;
text-decoration: none;
+ width:130px;
}
.segmentationContainer > ul.submenu > li {
padding: 5px 0;
diff --git a/plugins/SegmentEditor/templates/selector.twig b/plugins/SegmentEditor/templates/selector.twig
index 0cabb467e5..86939a1480 100644
--- a/plugins/SegmentEditor/templates/selector.twig
+++ b/plugins/SegmentEditor/templates/selector.twig
@@ -1,29 +1,38 @@
<div id="SegmentEditor" style="display:none;">
<div class="segmentationContainer listHtml">
- <span class="segmentationTitle"><b>Add segment</b></span>
+ <span class="segmentationTitle"></span>
+
<ul class="submenu">
- <li> Select a segment of visitors
+ <li>{{ 'SegmentEditor_SelectSegmentOfVisitors'|translate }}
<div class="segmentList">
<ul>
</ul>
</div>
</li>
</ul>
- <a class="add_new_segment">Add new segment</a>
+ {% if authorizedToCreateSegments %}
+ <a class="add_new_segment">{{ 'SegmentEditor_AddNewSegment'|translate }}</a>
+ {% else %}
+ <ul class="submenu">
+ <li> <span class='youMustBeLoggedIn'>{{ 'SegmentEditor_YouMustBeLoggedInToCreateSegments'|translate }}
+ <br/>&rsaquo; <a href='index.php?module={{ loginModule }}'>{{ 'Login_LogIn'|translate }}</a> </span></strong>
+ </li>
+ </ul>
+ {% endif %}
</div>
<div class="initial-state-rows">{# no space here important for jquery #}<div class="segment-add-row initial"><div>
- <span>+ Drag &amp; Drop condition</span>
+ <span>+ {{ 'SegmentEditor_DragDropCondition'|translate }}</span>
</div></div>
- <div class="segment-and">AND</div>
+ <div class="segment-and">{{ 'SegmentEditor_OperatorAND'|translate }}</div>
<div class="segment-add-row initial"><div>
- <span>+ Drag &amp; Drop condition</span>
+ <span>+ {{ 'SegmentEditor_DragDropCondition'|translate }}</span>
</div></div>
</div>
<div class="segment-row-inputs">
<div class="segment-input metricListBlock">
- <select title="Choose a segment" class="metricList">
+ <select title="{{ 'SegmentEditor_ChooseASegment'|translate }}" class="metricList">
{% for category,segmentsInCategory in segmentsByCategory %}
<optgroup label="{{ category }}">
{% for segmentInCategory in segmentsInCategory %}
@@ -34,19 +43,19 @@
</select>
</div>
<div class="segment-input metricMatchBlock">
- <select title="Matches">
- <option value="==">Equals</option>
- <option value="!=">Not Equals</option>
- <option value="&lt;=">At most</option>
- <option value="&gt;=">At least</option>
- <option value="&lt;">Less</option>
- <option value="&gt;">Greater</option>
- <option value="=@">Contains</option>
- <option value="!@">Does not contain</option>
+ <select title="{{ 'General_Matches'|translate }}">
+ <option value="==">{{ 'General_OperationEquals'|translate }}</option>
+ <option value="!=">{{ 'General_OperationNotEquals'|translate }}</option>
+ <option value="<=">{{ 'General_OperationAtMost'|translate }}</option>
+ <option value=">=">{{ 'General_OperationAtLeast'|translate }}</option>
+ <option value="<">{{ 'General_OperationLessThan'|translate }}</option>
+ <option value=">">{{ 'General_OperationGreaterThan'|translate }}</option>
+ <option value="=@">{{ 'General_OperationContains'|translate }}</option>
+ <option value="!@">{{ 'General_OperationDoesNotContain'|translate }}</option>
</select>
</div>
<div class="segment-input metricValueBlock">
- <input type="text" title="Value">
+ <input type="text" title="{{ 'General_Value'|translate }}">
</div>
<div class="clear"></div>
</div>
@@ -56,14 +65,16 @@
<a href="#" class="segment-loading"></a>
</div>
</div>
- <div class="segment-or">OR</div>
+ <div class="segment-or">{{ 'SegmentEditor_OperatorOR'|translate }}</div>
<div class="segment-add-or"><div>
- <a href="#"> + Add <span>OR</span> condition </a>
+ {% set orCondition %}<span>{{ 'SegmentEditor_OperatorOR'|translate }}</span>{% endset %}
+ <a href="#"> + {{ 'SegmentEditor_AddANDorORCondition'|translate(orCondition) }} </a>
</div>
</div>
- <div class="segment-and">AND</div>
+ <div class="segment-and">{{ 'SegmentEditor_OperatorAND'|translate }}</div>
<div class="segment-add-row"><div>
- <a href="#">+ Add <span>AND</span> condition </a>
+ {% set andCondition %}<span>{{ 'SegmentEditor_OperatorAND'|translate }}</span>{% endset %}
+ <a href="#">+ {{ 'SegmentEditor_AddANDorORCondition'|translate(andCondition) }}</a>
</div>
</div>
<div style="position: absolute; z-index:999; width:1040px;" class="segment-element">
@@ -87,33 +98,41 @@
</div>
<div class="custom_select_search">
<a href="#"></a>
- <input type="text" aria-haspopup="true" aria-autocomplete="list" role="textbox" autocomplete="off" class="inp ui-autocomplete-input" id="segmentSearch" value="Search" length="15">
+ <input type="text" aria-haspopup="true" aria-autocomplete="list" role="textbox" autocomplete="off" class="inp ui-autocomplete-input" id="segmentSearch" value="{{ 'General_Search'|translate }}" length="15">
</div>
</div>
<div class="segment-content">
{% if isSuperUser %}
<div class="segment-top">
- This segment is visible to: <span id="enabledAllUsers"><strong>
- <select id="enabledAllUsers_select">
- <option selected="" value="0">me</option>
- <option value="1">All users</option>
+ {{ 'SegmentEditor_ThisSegmentIsVisibleTo'|translate }} <span id="enable_all_users"><strong>
+ <select id="enable_all_users_select">
+ <option selected="1" value="0">{{ 'SegmentEditor_VisibleToMe'|translate }}</option>
+ <option value="1">{{ 'SegmentEditor_VisibleToAllUsers'|translate }}</option>
</select>
</strong></span>
- and displayed for <span id="visible_to_website"><strong>
+ {{ 'SegmentEditor_SegmentIsDisplayedForWebsite'|translate }}<span id="visible_to_website"><strong>
<select id="visible_to_website_select">
- <option selected="" value="{{ idSite }}">this website only</option>
- <option value="0">all websites</option>
+ <option selected="" value="{{ idSite }}">{{ 'SegmentEditor_SegmentDisplayedThisWebsiteOnly'|translate }}</option>
+ <option value="0">{{ 'SegmentEditor_SegmentDisplayedAllWebsites'|translate }}</option>
</select>
</strong></span>
+ {{ 'General_And'|translate }} <span id="auto_archive"><strong>
+ <select id="auto_archive_select">
+ <option selected="1" value="0">{{ 'SegmentEditor_AutoArchiveRealTime'|translate }} {{ 'General_DefaultAppended'|translate }}</option>
+ <option value="1">{{ 'SegmentEditor_AutoArchivePreProcessed'|translate }} </option>
+ </select>
+ </strong></span>
+
</div>
{% endif %}
- <h3>Name: <span>New segment</span> <a class="editSegmentName" href="#">edit</a></h3>
+ <h3>{{ 'General_Name'|translate }}: <span class="segmentName"></span> <a class="editSegmentName" href="#">{{ 'General_Edit'|translate|lower }}</a></h3>
</div>
<div class="segment-footer">
- <a class="delete" href="#">Delete</a>
- <a class="close" href="#">Close</a>
- <button class="saveAndApply">Save & Apply</button>
+ <span class="segmentFooterNote">The Segment Editor was <a class='crowdfundingLink' href='http://crowdfunding.piwik.org/custom-segments-editor/' target='_blank'>crowdfunded</a> with the awesome support of 80 companies and Piwik users worldwide!</span>
+ <a class="delete" href="#">{{ 'General_Delete'|translate }}</a>
+ <a class="close" href="#">{{ 'General_Close'|translate }}</a>
+ <button class="saveAndApply">{{ 'SegmentEditor_SaveAndApply'|translate }}</button>
</div>
</div>
</div>
@@ -122,12 +141,13 @@
<div id="segmentList"></div>
</span>
-<div class="ui-confirm" id="confirm">
- <h2>Are you sure you want to delete this segment?</h2>
+<div class="ui-confirm" id="segment-delete-confirm">
+ <h2>{{ 'SegmentEditor_AreYouSureDeleteSegment'|translate }}</h2>
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
<input role="no" type="button" value="{{ 'General_No'|translate }}"/>
</div>
<script type="text/javascript">
var availableSegments = {{ savedSegmentsJson|raw }};
+var segmentTranslations = {{ segmentTranslations|raw }};
</script>
diff --git a/plugins/SitesManager/javascripts/SitesManager.js b/plugins/SitesManager/javascripts/SitesManager.js
index 4d32b283f3..4ff38d2159 100644
--- a/plugins/SitesManager/javascripts/SitesManager.js
+++ b/plugins/SitesManager/javascripts/SitesManager.js
@@ -117,8 +117,8 @@ function SitesManager(_timezones, _currencies, _defaultTimezone, _defaultCurrenc
}
function sendGlobalSettingsAJAX() {
- var timezone = $('#defaultTimezone option:selected').val();
- var currency = $('#defaultCurrency option:selected').val();
+ var timezone = $('#defaultTimezone').find('option:selected').val();
+ var currency = $('#defaultCurrency').find('option:selected').val();
var excludedIps = $('textarea#globalExcludedIps').val();
excludedIps = piwikHelper.getApiFormatTextarea(excludedIps);
var excludedQueryParameters = $('textarea#globalExcludedQueryParameters').val();
@@ -157,6 +157,11 @@ function SitesManager(_timezones, _currencies, _defaultTimezone, _defaultCurrenc
$('.addRowSite').click(function () {
piwikHelper.hideAjaxError();
$('.addRowSite').toggle();
+
+ var excludedUserAgentCell = '';
+ if ($('#exclude-user-agent-header').is(':visible')) {
+ excludedUserAgentCell = '<td><textarea cols="20" rows="4" id="excludedUserAgents"></textarea><br />' + excludedUserAgentsHelp + '</td>';
+ }
var numberOfRows = $('table#editSites')[0].rows.length;
var newRowId = 'rowNew' + numberOfRows;
@@ -166,9 +171,9 @@ function SitesManager(_timezones, _currencies, _defaultTimezone, _defaultCurrenc
<td><input id="name" value="Name" size="15" /><br/><br/><br/>' + submitButtonHtml + '</td>\
<td><textarea cols="25" rows="3" id="urls">http://siteUrl.com/\nhttp://siteUrl2.com/</textarea><br />' + aliasUrlsHelp + keepURLFragmentSelectHTML + '</td>\
<td><textarea cols="20" rows="4" id="excludedIps"></textarea><br />' + excludedIpHelp + '</td>\
- <td><textarea cols="20" rows="4" id="excludedQueryParameters"></textarea><br />' + excludedQueryParametersHelp + '</td>\
- <td><textarea cols="20" rows="4" id="excludedUserAgents"></textarea><br />' + excludedUserAgentsHelp + '</td>\
- <td>' + getSitesearchSelector(false) + '</td>\
+ <td><textarea cols="20" rows="4" id="excludedQueryParameters"></textarea><br />' + excludedQueryParametersHelp + '</td>' +
+ excludedUserAgentCell +
+ '<td>' + getSitesearchSelector(false) + '</td>\
<td>' + getTimezoneSelector(defaultTimezone) + '<br />' + timezoneHelp + '</td>\
<td>' + getCurrencySelector(defaultCurrency) + '<br />' + currencyHelp + '</td>\
<td>' + getEcommerceSelector(0) + '<br />' + ecommerceHelp + '</td>\
@@ -199,7 +204,7 @@ function SitesManager(_timezones, _currencies, _defaultTimezone, _defaultCurrenc
var nameToDelete = $(this).parent().parent().find('input#siteName').val() || $(this).parent().parent().find('td#siteName').html();
var idsiteToDelete = $(this).parent().parent().find('#idSite').html();
- $('#confirm h2').text(sprintf(_pk_translate('SitesManager_DeleteConfirm_js'), '"' + nameToDelete + '" (idSite = ' + idsiteToDelete + ')'));
+ $('#confirm').find('h2').text(sprintf(_pk_translate('SitesManager_DeleteConfirm_js'), '"' + nameToDelete + '" (idSite = ' + idsiteToDelete + ')'));
piwikHelper.modalConfirm('#confirm', {yes: function () {
sendDeleteSiteAJAX(idsiteToDelete);
}});
@@ -213,7 +218,7 @@ function SitesManager(_timezones, _currencies, _defaultTimezone, _defaultCurrenc
var idRow = $(this).attr('id');
if (alreadyEdited[idRow] == 1) return;
if (siteBeingEdited) {
- $('#alert h2').text(sprintf(_pk_translate('SitesManager_OnlyOneSiteAtTime_js'), '"' + $("<div/>").html(siteBeingEditedName).text() + '"'));
+ $('#alert').find('h2').text(sprintf(_pk_translate('SitesManager_OnlyOneSiteAtTime_js'), '"' + $("<div/>").html(siteBeingEditedName).text() + '"'));
piwikHelper.modalConfirm('#alert', {});
return;
}
diff --git a/plugins/Transitions/API.php b/plugins/Transitions/API.php
index 4457d71046..1c2934ca74 100644
--- a/plugins/Transitions/API.php
+++ b/plugins/Transitions/API.php
@@ -62,15 +62,14 @@ class Piwik_Transitions_API
}
// prepare archive processing that can be used by the archiving code
- $archiveProcessing = new Piwik_ArchiveProcessing_Day();
- $archiveProcessing->setSite(new Piwik_Site($idSite));
- $archiveProcessing->setPeriod(Piwik_Period::advancedFactory($period, $date));
- $archiveProcessing->setSegment(new Piwik_Segment($segment, $idSite));
- $archiveProcessing->initForLiveUsage();
-
+ $segment = new Piwik_Segment($segment, $idSite);
+ $site = new Piwik_Site($idSite);
+ $period = Piwik_Period::advancedFactory($period, $date);
+ $archiveProcessor = new Piwik_ArchiveProcessor_Day($period, $site, $segment);
+ $logAggregator = $archiveProcessor->getLogAggregator();
// prepare the report
$report = array(
- 'date' => Piwik_Period_Day::advancedFactory($period, $date)->getLocalizedShortString()
+ 'date' => Piwik_Period_Day::advancedFactory($period->getLabel(), $date)->getLocalizedShortString()
);
// add data to the report
@@ -82,23 +81,20 @@ class Piwik_Transitions_API
$partsArray = explode(',', $parts);
if ($parts == 'all' || in_array('internalReferrers', $partsArray)) {
- $this->addInternalReferrers($transitionsArchiving, $archiveProcessing, $report, $idaction,
- $actionType, $limitBeforeGrouping);
+ $this->addInternalReferrers($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping);
}
if ($parts == 'all' || in_array('followingActions', $partsArray)) {
$includeLoops = $parts != 'all' && !in_array('internalReferrers', $partsArray);
- $this->addFollowingActions($transitionsArchiving, $archiveProcessing, $report, $idaction,
- $actionType, $limitBeforeGrouping, $includeLoops);
+ $this->addFollowingActions($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping, $includeLoops);
}
if ($parts == 'all' || in_array('externalReferrers', $partsArray)) {
- $this->addExternalReferrers($transitionsArchiving, $archiveProcessing, $report, $idaction,
- $actionType, $limitBeforeGrouping);
+ $this->addExternalReferrers($logAggregator, $report, $idaction, $actionType, $limitBeforeGrouping);
}
// derive the number of exits from the other metrics
if ($parts == 'all') {
$report['pageMetrics']['exits'] = $report['pageMetrics']['pageviews']
- - $transitionsArchiving->getTotalTransitionsToFollowingActions()
+ - $this->getTotalTransitionsToFollowingActions()
- $report['pageMetrics']['loops'];
}
@@ -113,9 +109,9 @@ class Piwik_Transitions_API
);
foreach ($reportNames as $reportName => $replaceLabel) {
if (isset($report[$reportName])) {
- $columnNames = array(Piwik_Archive::INDEX_NB_ACTIONS => 'referrals');
+ $columnNames = array(Piwik_Metrics::INDEX_NB_ACTIONS => 'referrals');
if ($replaceLabel) {
- $columnNames[Piwik_Archive::INDEX_NB_ACTIONS] = 'referrals';
+ $columnNames[Piwik_Metrics::INDEX_NB_ACTIONS] = 'referrals';
}
$report[$reportName]->filter('ReplaceColumnNames', array($columnNames));
}
@@ -139,7 +135,7 @@ class Piwik_Transitions_API
if ($id < 0) {
// an example where this is needed is urls containing < or >
$actionName = $originalActionName;
- $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url', Piwik_SegmentExpression::MATCH_EQUALs, 'pageUrl');
+ $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url', Piwik_SegmentExpression::MATCH_EQUAL, 'pageUrl');
}
return $id;
@@ -167,20 +163,17 @@ class Piwik_Transitions_API
* Add the internal referrers to the report:
* previous pages and previous site searches
*
- * @param Piwik_Transitions $transitionsArchiving
- * @param $archiveProcessing
+ * @param Piwik_DataAccess_LogAggregator $logAggregator
* @param $report
* @param $idaction
* @param string $actionType
* @param $limitBeforeGrouping
* @throws Exception
*/
- private function addInternalReferrers($transitionsArchiving, $archiveProcessing, &$report,
- $idaction, $actionType, $limitBeforeGrouping)
+ private function addInternalReferrers($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping)
{
- $data = $transitionsArchiving->queryInternalReferrers(
- $idaction, $actionType, $archiveProcessing, $limitBeforeGrouping);
+ $data = $this->queryInternalReferrers($idaction, $actionType, $logAggregator, $limitBeforeGrouping);
if ($data['pageviews'] == 0) {
throw new Exception('NoDataForAction');
@@ -196,49 +189,375 @@ class Piwik_Transitions_API
* Add the following actions to the report:
* following pages, downloads, outlinks
*
- * @param Piwik_Transitions $transitionsArchiving
- * @param $archiveProcessing
+ * @param Piwik_DataAccess_LogAggregator $logAggregator
* @param $report
* @param $idaction
* @param string $actionType
* @param $limitBeforeGrouping
* @param boolean $includeLoops
*/
- private function addFollowingActions($transitionsArchiving, $archiveProcessing, &$report,
- $idaction, $actionType, $limitBeforeGrouping, $includeLoops = false)
+ private function addFollowingActions($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping, $includeLoops = false)
{
- $data = $transitionsArchiving->queryFollowingActions(
- $idaction, $actionType, $archiveProcessing, $limitBeforeGrouping, $includeLoops);
+ $data = $this->queryFollowingActions(
+ $idaction, $actionType, $logAggregator, $limitBeforeGrouping, $includeLoops);
foreach ($data as $tableName => $table) {
$report[$tableName] = $table;
}
}
+
+
+ /**
+ * Get information about the following actions (following pages, site searches, outlinks, downloads)
+ *
+ * @param $idaction
+ * @param $actionType
+ * @param Piwik_DataAccess_LogAggregator $logAggregator
+ * @param $limitBeforeGrouping
+ * @param $includeLoops
+ * @return array(followingPages:Piwik_DataTable, outlinks:Piwik_DataTable, downloads:Piwik_DataTable)
+ */
+ public function queryFollowingActions($idaction, $actionType, Piwik_DataAccess_LogAggregator $logAggregator,
+ $limitBeforeGrouping = false, $includeLoops = false)
+ {
+ $types = array();
+
+ $isTitle = ($actionType == 'title');
+ if (!$isTitle) {
+ // specific setup for page urls
+ $types[Piwik_Tracker_Action::TYPE_ACTION_URL] = 'followingPages';
+ $dimension = 'IF( idaction_url IS NULL, idaction_name, idaction_url )';
+ // site search referrers are logged with url=NULL
+ // when we find one, we have to join on name
+ $joinLogActionColumn = $dimension;
+ $selects = array('log_action.name', 'log_action.url_prefix', 'log_action.type');
+ } else {
+ // specific setup for page titles:
+ $types[Piwik_Tracker_Action::TYPE_ACTION_NAME] = 'followingPages';
+ // join log_action on name and url and pick depending on url type
+ // the table joined on url is log_action1
+ $joinLogActionColumn = array('idaction_url', 'idaction_name');
+ $dimension = '
+ CASE
+ ' /* following site search */ . '
+ WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.idaction
+ ' /* following page view: use page title */ . '
+ WHEN log_action1.type = ' . Piwik_Tracker_Action::TYPE_ACTION_URL . ' THEN log_action2.idaction
+ ' /* following download or outlink: use url */ . '
+ ELSE log_action1.idaction
+ END
+ ';
+ $selects = array(
+ 'CASE
+ ' /* following site search */ . '
+ WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.name
+ ' /* following page view: use page title */ . '
+ WHEN log_action1.type = ' . Piwik_Tracker_Action::TYPE_ACTION_URL . ' THEN log_action2.name
+ ' /* following download or outlink: use url */ . '
+ ELSE log_action1.name
+ END AS `name`',
+ 'CASE
+ ' /* following site search */ . '
+ WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.type
+ ' /* following page view: use page title */ . '
+ WHEN log_action1.type = ' . Piwik_Tracker_Action::TYPE_ACTION_URL . ' THEN log_action2.type
+ ' /* following download or outlink: use url */ . '
+ ELSE log_action1.type
+ END AS `type`',
+ 'NULL AS `url_prefix`'
+ );
+ }
+
+ // these types are available for both titles and urls
+ $types[Piwik_Tracker_Action::TYPE_SITE_SEARCH] = 'followingSiteSearches';
+ $types[Piwik_Tracker_Action::TYPE_OUTLINK] = 'outlinks';
+ $types[Piwik_Tracker_Action::TYPE_DOWNLOAD] = 'downloads';
+
+ $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
+ $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
+ $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($types));
+
+ $type = $this->getColumnTypeSuffix($actionType);
+ $where = 'log_link_visit_action.idaction_' . $type . '_ref = ' . intval($idaction);
+ if (!$includeLoops) {
+ $where .= ' AND (log_link_visit_action.idaction_' . $type . ' IS NULL OR '
+ . 'log_link_visit_action.idaction_' . $type . ' != ' . intval($idaction) . ')';
+ }
+
+ $metrics = array(Piwik_Metrics::INDEX_NB_ACTIONS);
+ $data = $logAggregator->queryActionsByDimension(array($dimension), $where, $selects, $metrics, $rankingQuery, $joinLogActionColumn);
+
+ $this->totalTransitionsToFollowingActions = 0;
+ $dataTables = array();
+ foreach ($types as $type => $recordName) {
+ $dataTable = new Piwik_DataTable;
+ if (isset($data[$type])) {
+ foreach ($data[$type] as &$record) {
+ $actions = intval($record[Piwik_Metrics::INDEX_NB_ACTIONS]);
+ $dataTable->addRow(new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => array(
+ 'label' => $this->getPageLabel($record, $isTitle),
+ Piwik_Metrics::INDEX_NB_ACTIONS => $actions
+ )
+ )));
+ $this->totalTransitionsToFollowingActions += $actions;
+ }
+ }
+ $dataTables[$recordName] = $dataTable;
+ }
+
+ return $dataTables;
+ }
+
+
+ /**
+ * After calling this method, the query*()-Methods will return urls in their
+ * normalized form (without the prefix reconstructed)
+ */
+ public function returnNormalizedUrls()
+ {
+ $this->returnNormalizedUrls = true;
+ }
+
+ /**
+ * Get information about external referrers (i.e. search engines, websites & campaigns)
+ *
+ * @param $idaction
+ * @param $actionType
+ * @param Piwik_ArchiveProcessor_Day $logAggregator
+ * @param $limitBeforeGrouping
+ * @return Piwik_DataTable
+ */
+ public function queryExternalReferrers($idaction, $actionType, $logAggregator,
+ $limitBeforeGrouping = false)
+ {
+ $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
+
+ // we generate a single column that contains the interesting data for each referrer.
+ // the reason we cannot group by referer_* becomes clear when we look at search engine keywords.
+ // referer_url contains the url from the search engine, referer_keyword the keyword we want to
+ // group by. when we group by both, we don't get a single column for the keyword but instead
+ // one column per keyword + search engine url. this way, we could not get the top keywords using
+ // the ranking query.
+ $dimensions = array('referrer_data', 'referer_type');
+ $rankingQuery->addLabelColumn('referrer_data');
+ $selects = array(
+ 'CASE referer_type
+ WHEN ' . Piwik_Common::REFERER_TYPE_DIRECT_ENTRY . ' THEN \'\'
+ WHEN ' . Piwik_Common::REFERER_TYPE_SEARCH_ENGINE . ' THEN referer_keyword
+ WHEN ' . Piwik_Common::REFERER_TYPE_WEBSITE . ' THEN referer_url
+ WHEN ' . Piwik_Common::REFERER_TYPE_CAMPAIGN . ' THEN CONCAT(referer_name, \' \', referer_keyword)
+ END AS `referrer_data`');
+
+ // get one limited group per referrer type
+ $rankingQuery->partitionResultIntoMultipleGroups('referer_type', array(
+ Piwik_Common::REFERER_TYPE_DIRECT_ENTRY,
+ Piwik_Common::REFERER_TYPE_SEARCH_ENGINE,
+ Piwik_Common::REFERER_TYPE_WEBSITE,
+ Piwik_Common::REFERER_TYPE_CAMPAIGN
+ ));
+
+ $type = $this->getColumnTypeSuffix($actionType);
+ $where = 'visit_entry_idaction_' . $type . ' = ' . intval($idaction);
+
+ $metrics = array(Piwik_Metrics::INDEX_NB_VISITS);
+ $data = $logAggregator->queryVisitsByDimension($dimensions, $where, $selects, $metrics, $rankingQuery);
+
+ $referrerData = array();
+ $referrerSubData = array();
+
+ foreach ($data as $referrerType => &$subData) {
+ $referrerData[$referrerType] = array(Piwik_Metrics::INDEX_NB_VISITS => 0);
+ if ($referrerType != Piwik_Common::REFERER_TYPE_DIRECT_ENTRY) {
+ $referrerSubData[$referrerType] = array();
+ }
+
+ foreach ($subData as &$row) {
+ if ($referrerType == Piwik_Common::REFERER_TYPE_SEARCH_ENGINE && empty($row['referrer_data'])) {
+ $row['referrer_data'] = Piwik_Referers_API::LABEL_KEYWORD_NOT_DEFINED;
+ }
+
+ $referrerData[$referrerType][Piwik_Metrics::INDEX_NB_VISITS] += $row[Piwik_Metrics::INDEX_NB_VISITS];
+
+ $label = $row['referrer_data'];
+ if ($label) {
+ $referrerSubData[$referrerType][$label] = array(
+ Piwik_Metrics::INDEX_NB_VISITS => $row[Piwik_Metrics::INDEX_NB_VISITS]
+ );
+ }
+ }
+ }
+
+ //FIXMEA refactor after integration tests written
+ $array = new Piwik_DataArray($referrerData, $referrerSubData);
+ return Piwik_ArchiveProcessor_Day::getDataTableFromDataArray($array);
+ }
+
+ /**
+ * Get information about internal referrers (previous pages & loops, i.e. page refreshes)
+ *
+ * @param $idaction
+ * @param $actionType
+ * @param Piwik_ArchiveProcessor_Day $logAggregator
+ * @param $limitBeforeGrouping
+ * @return array(previousPages:Piwik_DataTable, loops:integer)
+ */
+ protected function queryInternalReferrers($idaction, $actionType, $logAggregator,
+ $limitBeforeGrouping = false)
+ {
+ $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
+ $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
+ $rankingQuery->setColumnToMarkExcludedRows('is_self');
+ $rankingQuery->partitionResultIntoMultipleGroups('action_partition', array(0, 1, 2));
+
+ $type = $this->getColumnTypeSuffix($actionType);
+ $mainActionType = Piwik_Tracker_Action::TYPE_ACTION_URL;
+ $dimension = 'idaction_url_ref';
+ $isTitle = $actionType == 'title';
+
+ if ($isTitle) {
+ $mainActionType = Piwik_Tracker_Action::TYPE_ACTION_NAME;
+ $dimension = 'idaction_name_ref';
+ }
+
+ $selects = array(
+ 'log_action.name',
+ 'log_action.url_prefix',
+ 'CASE WHEN log_link_visit_action.idaction_' . $type . '_ref = ' . intval($idaction) . ' THEN 1 ELSE 0 END AS `is_self`',
+ 'CASE
+ WHEN log_action.type = ' . $mainActionType . ' THEN 1
+ WHEN log_action.type = ' . Piwik_Tracker_Action::TYPE_SITE_SEARCH . ' THEN 2
+ ELSE 0
+ END AS `action_partition`'
+ );
+
+ $where = '
+ log_link_visit_action.idaction_' . $type . ' = ' . intval($idaction);
+
+ if ($dimension == 'idaction_url_ref') {
+ // site search referrers are logged with url_ref=NULL
+ // when we find one, we have to join on name_ref
+ $dimension = 'IF( idaction_url_ref IS NULL, idaction_name_ref, idaction_url_ref )';
+ $joinLogActionOn = $dimension;
+ } else {
+ $joinLogActionOn = $dimension;
+ }
+ $metrics = array(Piwik_Metrics::INDEX_NB_ACTIONS);
+ $data = $logAggregator->queryActionsByDimension(array($dimension), $where, $selects, $metrics, $rankingQuery, $joinLogActionOn);
+
+ $loops = 0;
+ $nbPageviews = 0;
+ $previousPagesDataTable = new Piwik_DataTable;
+ if (isset($data['result'][1])) {
+ foreach ($data['result'][1] as &$page) {
+ $nbActions = intval($page[Piwik_Metrics::INDEX_NB_ACTIONS]);
+ $previousPagesDataTable->addRow(new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => array(
+ 'label' => $this->getPageLabel($page, $isTitle),
+ Piwik_Metrics::INDEX_NB_ACTIONS => $nbActions
+ )
+ )));
+ $nbPageviews += $nbActions;
+ }
+ }
+
+ $previousSearchesDataTable = new Piwik_DataTable;
+ if (isset($data['result'][2])) {
+ foreach ($data['result'][2] as &$search) {
+ $nbActions = intval($search[Piwik_Metrics::INDEX_NB_ACTIONS]);
+ $previousSearchesDataTable->addRow(new Piwik_DataTable_Row(array(
+ Piwik_DataTable_Row::COLUMNS => array(
+ 'label' => $search['name'],
+ Piwik_Metrics::INDEX_NB_ACTIONS => $nbActions
+ )
+ )));
+ $nbPageviews += $nbActions;
+ }
+ }
+
+ if (isset($data['result'][0])) {
+ foreach ($data['result'][0] as &$referrer) {
+ $nbPageviews += intval($referrer[Piwik_Metrics::INDEX_NB_ACTIONS]);
+ }
+ }
+
+ if (count($data['excludedFromLimit'])) {
+ $loops += intval($data['excludedFromLimit'][0][Piwik_Metrics::INDEX_NB_ACTIONS]);
+ $nbPageviews += $loops;
+ }
+
+ return array(
+ 'pageviews' => $nbPageviews,
+ 'previousPages' => $previousPagesDataTable,
+ 'previousSiteSearches' => $previousSearchesDataTable,
+ 'loops' => $loops
+ );
+ }
+
+ private function getPageLabel(&$pageRecord, $isTitle)
+ {
+ if ($isTitle) {
+ $label = $pageRecord['name'];
+ if (empty($label)) {
+ $label = Piwik_Actions_ArchivingHelper::getUnknownActionName(
+ Piwik_Tracker_Action::TYPE_ACTION_NAME);
+ }
+ return $label;
+ } else if ($this->returnNormalizedUrls) {
+ return $pageRecord['name'];
+ } else {
+ return Piwik_Tracker_Action::reconstructNormalizedUrl(
+ $pageRecord['name'], $pageRecord['url_prefix']);
+ }
+ }
+
+ private function getColumnTypeSuffix($actionType)
+ {
+ if ($actionType == 'title') {
+ return 'name';
+ }
+ return 'url';
+ }
+
+ private $limitBeforeGrouping = 5;
+ private $totalTransitionsToFollowingActions = 0;
+
+ private $returnNormalizedUrls = false;
+
+
+ /**
+ * Get the sum of all transitions to following actions (pages, outlinks, downloads).
+ * Only works if queryFollowingActions() has been used directly before.
+ */
+ protected function getTotalTransitionsToFollowingActions()
+ {
+ return $this->totalTransitionsToFollowingActions;
+ }
+
/**
* Add the external referrers to the report:
* direct entries, websites, campaigns, search engines
*
- * @param Piwik_Transitions $transitionsArchiving
- * @param $archiveProcessing
+ * @param Piwik_DataAccess_LogAggregator $logAggregator
* @param $report
* @param $idaction
* @param string $actionType
* @param $limitBeforeGrouping
*/
- private function addExternalReferrers($transitionsArchiving, $archiveProcessing, &$report,
+ private function addExternalReferrers($logAggregator, &$report,
$idaction, $actionType, $limitBeforeGrouping)
{
- $data = $transitionsArchiving->queryExternalReferrers(
- $idaction, $actionType, $archiveProcessing, $limitBeforeGrouping);
+ $data = $this->queryExternalReferrers(
+ $idaction, $actionType, $logAggregator, $limitBeforeGrouping);
$report['pageMetrics']['entries'] = 0;
$report['referrers'] = array();
foreach ($data->getRows() as $row) {
$referrerId = $row->getColumn('label');
- $visits = $row->getColumn(Piwik_Archive::INDEX_NB_VISITS);
+ $visits = $row->getColumn(Piwik_Metrics::INDEX_NB_VISITS);
if ($visits) {
// load details (i.e. subtables)
$details = array();
@@ -247,7 +566,7 @@ class Piwik_Transitions_API
foreach ($subTable->getRows() as $subRow) {
$details[] = array(
'label' => $subRow->getColumn('label'),
- 'referrals' => $subRow->getColumn(Piwik_Archive::INDEX_NB_VISITS)
+ 'referrals' => $subRow->getColumn(Piwik_Metrics::INDEX_NB_VISITS)
);
}
}
@@ -289,9 +608,6 @@ class Piwik_Transitions_API
}
}
- /**
- * @ignore
- */
public function getTranslations()
{
$controller = new Piwik_Transitions_Controller();
diff --git a/plugins/Transitions/Transitions.php b/plugins/Transitions/Transitions.php
index 0e43c1cef9..b503266fc3 100644
--- a/plugins/Transitions/Transitions.php
+++ b/plugins/Transitions/Transitions.php
@@ -15,11 +15,6 @@
class Piwik_Transitions extends Piwik_Plugin
{
- private $limitBeforeGrouping = 5;
- private $totalTransitionsToFollowingActions = 0;
-
- private $returnNormalizedUrls = false;
-
public function getInformation()
{
return array(
@@ -50,331 +45,5 @@ class Piwik_Transitions extends Piwik_Plugin
$jsFiles[] = 'plugins/Transitions/javascripts/transitions.js';
}
- /**
- * After calling this method, the query*()-Methods will return urls in their
- * normalized form (without the prefix reconstructed)
- */
- public function returnNormalizedUrls()
- {
- $this->returnNormalizedUrls = true;
- }
-
- /**
- * Get information about external referrers (i.e. search engines, websites & campaigns)
- *
- * @param $idaction
- * @param $actionType
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- * @param $limitBeforeGrouping
- * @return Piwik_DataTable
- */
- public function queryExternalReferrers($idaction, $actionType, $archiveProcessing,
- $limitBeforeGrouping = false)
- {
- $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
-
- // we generate a single column that contains the interesting data for each referrer.
- // the reason we cannot group by referer_* becomes clear when we look at search engine keywords.
- // referer_url contains the url from the search engine, referer_keyword the keyword we want to
- // group by. when we group by both, we don't get a single column for the keyword but instead
- // one column per keyword + search engine url. this way, we could not get the top keywords using
- // the ranking query.
- $dimension = 'referrer_data';
- $rankingQuery->addLabelColumn('referrer_data');
- $select = '
- CASE referer_type
- WHEN ' . Piwik_Common::REFERER_TYPE_DIRECT_ENTRY . ' THEN \'\'
- WHEN ' . Piwik_Common::REFERER_TYPE_SEARCH_ENGINE . ' THEN referer_keyword
- WHEN ' . Piwik_Common::REFERER_TYPE_WEBSITE . ' THEN referer_url
- WHEN ' . Piwik_Common::REFERER_TYPE_CAMPAIGN . ' THEN CONCAT(referer_name, \' \', referer_keyword)
- END AS referrer_data,
- referer_type';
-
- // get one limited group per referrer type
- $rankingQuery->partitionResultIntoMultipleGroups('referer_type', array(
- Piwik_Common::REFERER_TYPE_DIRECT_ENTRY,
- Piwik_Common::REFERER_TYPE_SEARCH_ENGINE,
- Piwik_Common::REFERER_TYPE_WEBSITE,
- Piwik_Common::REFERER_TYPE_CAMPAIGN
- ));
-
- $orderBy = '`' . Piwik_Archive::INDEX_NB_VISITS . '` DESC';
-
- $type = $this->getColumnTypeSuffix($actionType);
- $where = 'visit_entry_idaction_' . $type . ' = ' . intval($idaction);
-
- $metrics = array(Piwik_Archive::INDEX_NB_VISITS);
- $data = $archiveProcessing->queryVisitsByDimension($dimension, $where, $metrics, $orderBy,
- $rankingQuery, $select, $selectGeneratesLabelColumn = true);
-
- $referrerData = array();
- $referrerSubData = array();
-
- foreach ($data as $referrerType => &$subData) {
- $referrerData[$referrerType] = array(Piwik_Archive::INDEX_NB_VISITS => 0);
- if ($referrerType != Piwik_Common::REFERER_TYPE_DIRECT_ENTRY) {
- $referrerSubData[$referrerType] = array();
- }
-
- foreach ($subData as &$row) {
- if ($referrerType == Piwik_Common::REFERER_TYPE_SEARCH_ENGINE && empty($row['referrer_data'])) {
- $row['referrer_data'] = Piwik_Referers::LABEL_KEYWORD_NOT_DEFINED;
- }
-
- $referrerData[$referrerType][Piwik_Archive::INDEX_NB_VISITS] += $row[Piwik_Archive::INDEX_NB_VISITS];
-
- $label = $row['referrer_data'];
- if ($label) {
- $referrerSubData[$referrerType][$label] = array(
- Piwik_Archive::INDEX_NB_VISITS => $row[Piwik_Archive::INDEX_NB_VISITS]
- );
- }
- }
- }
-
- return $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($referrerSubData, $referrerData);
- }
-
- /**
- * Get information about internal referrers (previous pages & loops, i.e. page refreshes)
- *
- * @param $idaction
- * @param $actionType
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- * @param $limitBeforeGrouping
- * @return array(previousPages:Piwik_DataTable, loops:integer)
- */
- public function queryInternalReferrers($idaction, $actionType, $archiveProcessing,
- $limitBeforeGrouping = false)
- {
- $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
- $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
- $rankingQuery->setColumnToMarkExcludedRows('is_self');
- $rankingQuery->partitionResultIntoMultipleGroups('action_partition', array(0, 1, 2));
-
- $type = $this->getColumnTypeSuffix($actionType);
- $mainActionType = Piwik_Tracker_Action::TYPE_ACTION_URL;
- $dimension = 'idaction_url_ref';
- $isTitle = $actionType == 'title';
- if ($isTitle) {
- $mainActionType = Piwik_Tracker_Action::TYPE_ACTION_NAME;
- $dimension = 'idaction_name_ref';
- }
-
- $addSelect = '
- log_action.name, log_action.url_prefix,
- CASE WHEN log_link_visit_action.idaction_' . $type . '_ref = ' . intval($idaction) . ' THEN 1 ELSE 0 END AS is_self,
- CASE
- WHEN log_action.type = ' . $mainActionType . ' THEN 1
- WHEN log_action.type = ' . Piwik_Tracker_Action::TYPE_SITE_SEARCH . ' THEN 2
- ELSE 0
- END AS action_partition';
-
- $where = '
- log_link_visit_action.idaction_' . $type . ' = ' . intval($idaction);
-
- if ($dimension == 'idaction_url_ref') {
- // site search referrers are logged with url_ref=NULL
- // when we find one, we have to join on name_ref
- $dimension = 'IF( idaction_url_ref IS NULL, idaction_name_ref, idaction_url_ref )';
- $joinLogActionOn = $dimension;
- } else {
- $joinLogActionOn = $dimension;
- $dimension = array($dimension);
- }
-
- $orderBy = '`' . Piwik_Archive::INDEX_NB_ACTIONS . '` DESC';
-
- $metrics = array(Piwik_Archive::INDEX_NB_ACTIONS);
- $data = $archiveProcessing->queryActionsByDimension($dimension, $where, $metrics, $orderBy,
- $rankingQuery, $joinLogActionOn, $addSelect);
-
- $loops = 0;
- $nbPageviews = 0;
- $previousPagesDataTable = new Piwik_DataTable;
- if (isset($data['result'][1])) {
- foreach ($data['result'][1] as &$page) {
- $nbActions = intval($page[Piwik_Archive::INDEX_NB_ACTIONS]);
- $previousPagesDataTable->addRow(new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => array(
- 'label' => $this->getPageLabel($page, $isTitle),
- Piwik_Archive::INDEX_NB_ACTIONS => $nbActions
- )
- )));
- $nbPageviews += $nbActions;
- }
- }
-
- $previousSearchesDataTable = new Piwik_DataTable;
- if (isset($data['result'][2])) {
- foreach ($data['result'][2] as &$search) {
- $nbActions = intval($search[Piwik_Archive::INDEX_NB_ACTIONS]);
- $previousSearchesDataTable->addRow(new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => array(
- 'label' => $search['name'],
- Piwik_Archive::INDEX_NB_ACTIONS => $nbActions
- )
- )));
- $nbPageviews += $nbActions;
- }
- }
-
- if (isset($data['result'][0])) {
- foreach ($data['result'][0] as &$referrer) {
- $nbPageviews += intval($referrer[Piwik_Archive::INDEX_NB_ACTIONS]);
- }
- }
-
- if (count($data['excludedFromLimit'])) {
- $loops += intval($data['excludedFromLimit'][0][Piwik_Archive::INDEX_NB_ACTIONS]);
- $nbPageviews += $loops;
- }
-
- return array(
- 'pageviews' => $nbPageviews,
- 'previousPages' => $previousPagesDataTable,
- 'previousSiteSearches' => $previousSearchesDataTable,
- 'loops' => $loops
- );
- }
-
- private function getPageLabel(&$pageRecord, $isTitle)
- {
- if ($isTitle) {
- $label = $pageRecord['name'];
- if (empty($label)) {
- $label = Piwik_Actions_ArchivingHelper::getUnknownActionName(
- Piwik_Tracker_Action::TYPE_ACTION_NAME);
- }
- return $label;
- } else if ($this->returnNormalizedUrls) {
- return $pageRecord['name'];
- } else {
- return Piwik_Tracker_Action::reconstructNormalizedUrl(
- $pageRecord['name'], $pageRecord['url_prefix']);
- }
- }
-
- /**
- * Get information about the following actions (following pages, site searches, outlinks, downloads)
- *
- * @param $idaction
- * @param $actionType
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- * @param $limitBeforeGrouping
- * @param $includeLoops
- * @return array(followingPages:Piwik_DataTable, outlinks:Piwik_DataTable, downloads:Piwik_DataTable)
- */
- public function queryFollowingActions($idaction, $actionType, Piwik_ArchiveProcessing_Day $archiveProcessing,
- $limitBeforeGrouping = false, $includeLoops = false)
- {
- $types = array();
-
- $isTitle = ($actionType == 'title');
- if (!$isTitle) {
- // specific setup for page urls
- $types[Piwik_Tracker_Action::TYPE_ACTION_URL] = 'followingPages';
- $dimension = 'IF( idaction_url IS NULL, idaction_name, idaction_url )';
- // site search referrers are logged with url=NULL
- // when we find one, we have to join on name
- $joinLogActionColumn = $dimension;
- $addSelect = 'log_action.name, log_action.url_prefix, log_action.type';
- } else {
- // specific setup for page titles:
- $types[Piwik_Tracker_Action::TYPE_ACTION_NAME] = 'followingPages';
- // join log_action on name and url and pick depending on url type
- // the table joined on url is log_action1
- $joinLogActionColumn = array('idaction_url', 'idaction_name');
- $dimension = '
- CASE
- ' /* following site search */ . '
- WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.idaction
- ' /* following page view: use page title */ . '
- WHEN log_action1.type = ' . Piwik_Tracker_Action::TYPE_ACTION_URL . ' THEN log_action2.idaction
- ' /* following download or outlink: use url */ . '
- ELSE log_action1.idaction
- END
- ';
- $addSelect = '
- CASE
- ' /* following site search */ . '
- WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.name
- ' /* following page view: use page title */ . '
- WHEN log_action1.type = ' . Piwik_Tracker_Action::TYPE_ACTION_URL . ' THEN log_action2.name
- ' /* following download or outlink: use url */ . '
- ELSE log_action1.name
- END AS name,
- CASE
- ' /* following site search */ . '
- WHEN log_link_visit_action.idaction_url IS NULL THEN log_action2.type
- ' /* following page view: use page title */ . '
- WHEN log_action1.type = ' . Piwik_Tracker_Action::TYPE_ACTION_URL . ' THEN log_action2.type
- ' /* following download or outlink: use url */ . '
- ELSE log_action1.type
- END AS type,
- NULL AS url_prefix
- ';
- }
-
- // these types are available for both titles and urls
- $types[Piwik_Tracker_Action::TYPE_SITE_SEARCH] = 'followingSiteSearches';
- $types[Piwik_Tracker_Action::TYPE_OUTLINK] = 'outlinks';
- $types[Piwik_Tracker_Action::TYPE_DOWNLOAD] = 'downloads';
-
- $rankingQuery = new Piwik_RankingQuery($limitBeforeGrouping ? $limitBeforeGrouping : $this->limitBeforeGrouping);
- $rankingQuery->addLabelColumn(array('name', 'url_prefix'));
- $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($types));
-
- $type = $this->getColumnTypeSuffix($actionType);
- $where = 'log_link_visit_action.idaction_' . $type . '_ref = ' . intval($idaction);
- if (!$includeLoops) {
- $where .= ' AND (log_link_visit_action.idaction_' . $type . ' IS NULL OR '
- . 'log_link_visit_action.idaction_' . $type . ' != ' . intval($idaction) . ')';
- }
-
- $orderBy = '`' . Piwik_Archive::INDEX_NB_ACTIONS . '` DESC';
-
- $metrics = array(Piwik_Archive::INDEX_NB_ACTIONS);
- $data = $archiveProcessing->queryActionsByDimension($dimension, $where, $metrics, $orderBy,
- $rankingQuery, $joinLogActionColumn, $addSelect);
-
- $this->totalTransitionsToFollowingActions = 0;
- $dataTables = array();
- foreach ($types as $type => $recordName) {
- $dataTable = new Piwik_DataTable;
- if (isset($data[$type])) {
- foreach ($data[$type] as &$record) {
- $actions = intval($record[Piwik_Archive::INDEX_NB_ACTIONS]);
- $dataTable->addRow(new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => array(
- 'label' => $this->getPageLabel($record, $isTitle),
- Piwik_Archive::INDEX_NB_ACTIONS => $actions
- )
- )));
- $this->totalTransitionsToFollowingActions += $actions;
- }
- }
- $dataTables[$recordName] = $dataTable;
- }
-
- return $dataTables;
- }
-
- /**
- * Get the sum of all transitions to following actions (pages, outlinks, downloads).
- * Only works if queryFollowingActions() has been used directly before.
- */
- public function getTotalTransitionsToFollowingActions()
- {
- return $this->totalTransitionsToFollowingActions;
- }
-
- private function getColumnTypeSuffix($actionType)
- {
- if ($actionType == 'title') {
- return 'name';
- }
- return 'url';
- }
} \ No newline at end of file
diff --git a/plugins/UserCountry/API.php b/plugins/UserCountry/API.php
index d8cdb266b6..261fda8f06 100644
--- a/plugins/UserCountry/API.php
+++ b/plugins/UserCountry/API.php
@@ -32,11 +32,9 @@ class Piwik_UserCountry_API
public function getCountry($idSite, $period, $date, $segment = false)
{
- $recordName = Piwik_UserCountry::VISITS_BY_COUNTRY_RECORD_NAME;
- $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserCountry_Archiver::COUNTRY_RECORD_NAME, $idSite, $period, $date, $segment);
- // apply filter on the whole datatable in order the inline search to work (searches
- // are done on "beautiful" label)
+ // apply filter on the whole datatable in order the inline search to work (searches are done on "beautiful" label)
$dataTable->filter('ColumnCallbackAddMetadata', array('label', 'code'));
$dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getFlagFromCode'));
$dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_CountryTranslate'));
@@ -48,8 +46,7 @@ class Piwik_UserCountry_API
public function getContinent($idSite, $period, $date, $segment = false)
{
- $recordName = Piwik_UserCountry::VISITS_BY_COUNTRY_RECORD_NAME;
- $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserCountry_Archiver::COUNTRY_RECORD_NAME, $idSite, $period, $date, $segment);
$getContinent = array('Piwik_Common', 'getContinent');
$dataTable->filter('GroupBy', array('label', $getContinent));
@@ -71,10 +68,9 @@ class Piwik_UserCountry_API
*/
public function getRegion($idSite, $period, $date, $segment = false)
{
- $recordName = Piwik_UserCountry::VISITS_BY_REGION_RECORD_NAME;
- $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserCountry_Archiver::REGION_RECORD_NAME, $idSite, $period, $date, $segment);
- $separator = Piwik_UserCountry::LOCATION_SEPARATOR;
+ $separator = Piwik_UserCountry_Archiver::LOCATION_SEPARATOR;
$unk = Piwik_Tracker_Visit::UNKNOWN_CODE;
// split the label and put the elements into the 'region' and 'country' metadata fields
@@ -114,10 +110,9 @@ class Piwik_UserCountry_API
*/
public function getCity($idSite, $period, $date, $segment = false)
{
- $recordName = Piwik_UserCountry::VISITS_BY_CITY_RECORD_NAME;
- $dataTable = $this->getDataTable($recordName, $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserCountry_Archiver::CITY_RECORD_NAME, $idSite, $period, $date, $segment);
- $separator = Piwik_UserCountry::LOCATION_SEPARATOR;
+ $separator = Piwik_UserCountry_Archiver::LOCATION_SEPARATOR;
$unk = Piwik_Tracker_Visit::UNKNOWN_CODE;
// split the label and put the elements into the 'city_name', 'region', 'country',
@@ -195,7 +190,7 @@ class Piwik_UserCountry_API
Piwik::checkUserHasViewAccess($idSite);
$archive = Piwik_Archive::build($idSite, $period, $date, $segment);
$dataTable = $archive->getDataTable($name);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
+ $dataTable->filter('Sort', array(Piwik_Metrics::INDEX_NB_VISITS));
$dataTable->queueFilter('ReplaceColumnNames');
return $dataTable;
}
@@ -204,6 +199,6 @@ class Piwik_UserCountry_API
{
Piwik::checkUserHasViewAccess($idSite);
$archive = Piwik_Archive::build($idSite, $period, $date, $segment);
- return $archive->getDataTableFromNumeric('UserCountry_distinctCountries');
+ return $archive->getDataTableFromNumeric(Piwik_UserCountry_Archiver::DISTINCT_COUNTRIES_METRIC);
}
}
diff --git a/plugins/UserCountry/Archiver.php b/plugins/UserCountry/Archiver.php
new file mode 100644
index 0000000000..2fa2573d55
--- /dev/null
+++ b/plugins/UserCountry/Archiver.php
@@ -0,0 +1,175 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_UserCountry
+ */
+
+class Piwik_UserCountry_Archiver extends Piwik_PluginsArchiver
+{
+ const COUNTRY_RECORD_NAME = 'UserCountry_country';
+ const REGION_RECORD_NAME = 'UserCountry_region';
+ const CITY_RECORD_NAME = 'UserCountry_city';
+ const DISTINCT_COUNTRIES_METRIC = 'UserCountry_distinctCountries';
+
+ // separate region, city & country info in stored report labels
+ const LOCATION_SEPARATOR = '|';
+
+ private $latLongForCities = array();
+
+ private $dataArrays = array();
+
+ protected $maximumRows;
+
+ const COUNTRY_FIELD = 'location_country';
+
+ const REGION_FIELD = 'location_region';
+
+ const CITY_FIELD = 'location_city';
+
+ protected $dimensions = array( self::COUNTRY_FIELD, self::REGION_FIELD, self::CITY_FIELD );
+
+ protected $arrays;
+ const LATITUDE_FIELD = 'location_latitude';
+ const LONGITUDE_FIELD = 'location_longitude';
+
+
+ public function archiveDay()
+ {
+ foreach($this->dimensions as $dimension) {
+ $this->arrays[$dimension] = new Piwik_DataArray();
+ }
+ $this->aggregateFromVisits();
+ $this->aggregateFromConversions();
+ $this->recordDayReports();
+ }
+
+ protected function aggregateFromVisits()
+ {
+ $additionalSelects = array('MAX(log_visit.location_latitude) as location_latitude',
+ 'MAX(log_visit.location_longitude) as location_longitude');
+ $query = $this->getLogAggregator()->queryVisitsByDimension($this->dimensions, $where = false, $additionalSelects);
+ if ($query === false) {
+ return;
+ }
+
+ while ($row = $query->fetch()) {
+ $this->makeRegionCityLabelsUnique($row);
+ $this->rememberCityLatLong($row);
+
+ /* @var $dataArray Piwik_DataArray */
+ foreach ($this->arrays as $dimension => $dataArray) {
+ $dataArray->sumMetricsVisits($row[$dimension], $row);
+ }
+ }
+ }
+
+ /**
+ * Makes sure the region and city of a query row are unique.
+ *
+ * @param array $row
+ */
+ private function makeRegionCityLabelsUnique(&$row)
+ {
+ // remove the location separator from the region/city/country we get from the query
+ foreach ($this->dimensions as $column) {
+ $row[$column] = str_replace(self::LOCATION_SEPARATOR, '', $row[$column]);
+ }
+
+ if (!empty($row[self::REGION_FIELD])) {
+ $row[self::REGION_FIELD] = $row[self::REGION_FIELD] . self::LOCATION_SEPARATOR . $row[self::COUNTRY_FIELD];
+ }
+
+ if (!empty($row[self::CITY_FIELD])) {
+ $row[self::CITY_FIELD] = $row[self::CITY_FIELD] . self::LOCATION_SEPARATOR . $row[self::REGION_FIELD];
+ }
+ }
+
+ protected function rememberCityLatLong($row)
+ {
+ if ( !empty($row[self::CITY_FIELD])
+ && !empty($row[self::LATITUDE_FIELD])
+ && !empty($row[self::LONGITUDE_FIELD])
+ && empty($this->latLongForCities[$row[self::CITY_FIELD]])) {
+ $this->latLongForCities[$row[self::CITY_FIELD]] = array($row[self::LATITUDE_FIELD], $row[self::LONGITUDE_FIELD]);
+ }
+ }
+
+ protected function aggregateFromConversions()
+ {
+ $query = $this->getLogAggregator()->queryConversionsByDimension($this->dimensions);
+
+ if ($query === false) {
+ return;
+ }
+
+ while ($row = $query->fetch()) {
+ $this->makeRegionCityLabelsUnique($row);
+
+ /* @var $dataArray Piwik_DataArray */
+ foreach ($this->arrays as $dimension => $dataArray) {
+ $dataArray->sumMetricsGoals($row[$dimension], $row);
+ }
+ }
+
+ /* @var $dataArray Piwik_DataArray */
+ foreach ($this->arrays as $dataArray) {
+ $dataArray->enrichMetricsWithConversions();
+ }
+ }
+
+ protected function recordDayReports()
+ {
+ $tableCountry = Piwik_ArchiveProcessor_Day::getDataTableFromDataArray($this->arrays[self::COUNTRY_FIELD]);
+ $this->getProcessor()->insertBlobRecord(self::COUNTRY_RECORD_NAME, $tableCountry->getSerialized());
+ $this->getProcessor()->insertNumericRecord(self::DISTINCT_COUNTRIES_METRIC, $tableCountry->getRowsCount());
+
+ $tableRegion = Piwik_ArchiveProcessor_Day::getDataTableFromDataArray($this->arrays[self::REGION_FIELD]);
+ $serialized = $tableRegion->getSerialized($this->maximumRows, $this->maximumRows, Piwik_Metrics::INDEX_NB_VISITS);
+ $this->getProcessor()->insertBlobRecord(self::REGION_RECORD_NAME, $serialized);
+
+ $tableCity = Piwik_ArchiveProcessor_Day::getDataTableFromDataArray($this->arrays[self::CITY_FIELD]);
+ $this->setLatitudeLongitude($tableCity);
+ $serialized = $tableCity->getSerialized($this->maximumRows, $this->maximumRows, Piwik_Metrics::INDEX_NB_VISITS);
+ $this->getProcessor()->insertBlobRecord(self::CITY_RECORD_NAME, $serialized);
+ }
+
+ /**
+ * Utility method, appends latitude/longitude pairs to city table labels, if that data
+ * exists for the city.
+ */
+ private function setLatitudeLongitude(Piwik_DataTable $tableCity)
+ {
+ foreach ($tableCity->getRows() as $row) {
+ $label = $row->getColumn('label');
+ if (isset($this->latLongForCities[$label])) {
+ // get lat/long for city
+ list($lat, $long) = $this->latLongForCities[$label];
+ $lat = round($lat, Piwik_UserCountry_LocationProvider::GEOGRAPHIC_COORD_PRECISION);
+ $long = round($long, Piwik_UserCountry_LocationProvider::GEOGRAPHIC_COORD_PRECISION);
+
+ // set latitude + longitude metadata
+ $row->setMetadata('lat', $lat);
+ $row->setMetadata('long', $long);
+ }
+ }
+ }
+
+ public function archivePeriod()
+ {
+ $dataTableToSum = array(
+ self::COUNTRY_RECORD_NAME,
+ self::REGION_RECORD_NAME,
+ self::CITY_RECORD_NAME,
+ );
+
+ $nameToCount = $this->getProcessor()->aggregateDataTableReports($dataTableToSum);
+ $this->getProcessor()->insertNumericRecord(self::DISTINCT_COUNTRIES_METRIC,
+ $nameToCount[self::COUNTRY_RECORD_NAME]['level0']);
+ }
+
+} \ No newline at end of file
diff --git a/plugins/UserCountry/UserCountry.php b/plugins/UserCountry/UserCountry.php
index a054dae31c..1e69e344e4 100644
--- a/plugins/UserCountry/UserCountry.php
+++ b/plugins/UserCountry/UserCountry.php
@@ -20,14 +20,6 @@ require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/GeoIPAutoUpdater.php';
*/
class Piwik_UserCountry extends Piwik_Plugin
{
- const VISITS_BY_COUNTRY_RECORD_NAME = 'UserCountry_country';
- const VISITS_BY_REGION_RECORD_NAME = 'UserCountry_region';
- const VISITS_BY_CITY_RECORD_NAME = 'UserCountry_city';
-
- const DISTINCT_COUNTRIES_METRIC = 'UserCountry_distinctCountries';
-
- // separate region, city & country info in stored report labels
- const LOCATION_SEPARATOR = '|';
public function getInformation()
{
@@ -299,187 +291,28 @@ class Piwik_UserCountry extends Piwik_Plugin
*/
function archivePeriod($notification)
{
- /**
- * @param Piwik_ArchiveProcessing_Period $archiveProcessing
- */
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array(
- self::VISITS_BY_COUNTRY_RECORD_NAME,
- self::VISITS_BY_REGION_RECORD_NAME,
- self::VISITS_BY_CITY_RECORD_NAME,
- );
+ $archiveProcessor = $notification->getNotificationObject();
- $nameToCount = $archiveProcessing->archiveDataTable($dataTableToSum);
- $archiveProcessing->insertNumericRecord(self::DISTINCT_COUNTRIES_METRIC,
- $nameToCount[self::VISITS_BY_COUNTRY_RECORD_NAME]['level0']);
+ $archiving = new Piwik_UserCountry_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
+ }
}
- private $interestTables = null;
- private $latLongForCities = null;
-
/**
* @param Piwik_Event_Notification $notification notification object
* @return mixed
*/
function archiveDay($notification)
{
- /**
- * @var Piwik_ArchiveProcessing
- */
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->interestTables = array('location_country' => array(),
- 'location_region' => array(),
- 'location_city' => array());
- $this->latLongForCities = array();
-
- $this->archiveDayAggregateVisits($archiveProcessing);
- $this->archiveDayAggregateGoals($archiveProcessing);
- $this->archiveDayRecordInDatabase($archiveProcessing);
-
- unset($this->interestTables);
- unset($this->latLongForCities);
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- protected function archiveDayAggregateVisits($archiveProcessing)
- {
- $dimensions = array_keys($this->interestTables);
- $query = $archiveProcessing->queryVisitsByDimension(
- $dimensions,
- $where = '',
- $metrics = false,
- $orderBy = false,
- $rankingQuery = null,
- $addSelect = 'MAX(log_visit.location_latitude) as location_latitude,
- MAX(log_visit.location_longitude) as location_longitude'
- );
-
- if ($query === false) {
- return;
- }
-
- while ($row = $query->fetch()) {
- // get latitude/longitude if there's a city
- $lat = $long = false;
- if (!empty($row['location_city'])) {
- if (!empty($row['location_latitude'])) {
- $lat = $row['location_latitude'];
- }
- if (!empty($row['location_longitude'])) {
- $long = $row['location_longitude'];
- }
- }
-
- // make sure regions & cities w/ the same name don't get merged
- $this->setLongCityRegionId($row);
-
- // store latitude/longitude, if we should
- if ($lat !== false && $long !== false) {
- $this->latLongForCities[$row['location_city']] = array($lat, $long);
- }
+ $archiveProcessor = $notification->getNotificationObject();
- // add the stats to each dimension's table
- foreach ($this->interestTables as $dimension => &$table) {
- $label = (string)$row[$dimension];
-
- if (!isset($table[$label])) {
- $table[$label] = $archiveProcessing->getNewInterestRow();
- }
- $archiveProcessing->updateInterestStats($row, $table[$label]);
- }
+ $archiving = new Piwik_UserCountry_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archiveDay();
}
}
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- protected function archiveDayAggregateGoals($archiveProcessing)
- {
- $dimensions = array_keys($this->interestTables);
- $query = $archiveProcessing->queryConversionsByDimension($dimensions);
-
- if ($query === false) {
- return;
- }
-
- while ($row = $query->fetch()) {
- // make sure regions & cities w/ the same name don't get merged
- $this->setLongCityRegionId($row);
-
- $idGoal = $row['idgoal'];
- foreach ($this->interestTables as $dimension => &$table) {
- $label = (string)$row[$dimension];
-
- if (!isset($table[$label][Piwik_Archive::INDEX_GOALS][$idGoal])) {
- $table[$label][Piwik_Archive::INDEX_GOALS][$idGoal] = $archiveProcessing->getNewGoalRow($idGoal);
- }
-
- $archiveProcessing->updateGoalStats($row, $table[$label][Piwik_Archive::INDEX_GOALS][$idGoal]);
- }
- }
-
- foreach ($this->interestTables as &$table) {
- $archiveProcessing->enrichConversionsByLabelArray($table);
- }
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- protected function archiveDayRecordInDatabase($archiveProcessing)
- {
- $maximumRows = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
-
- $tableCountry = Piwik_ArchiveProcessing_Day::getDataTableFromArray($this->interestTables['location_country']);
- $archiveProcessing->insertBlobRecord(self::VISITS_BY_COUNTRY_RECORD_NAME, $tableCountry->getSerialized());
- $archiveProcessing->insertNumericRecord(self::DISTINCT_COUNTRIES_METRIC, $tableCountry->getRowsCount());
- destroy($tableCountry);
-
- $tableRegion = Piwik_ArchiveProcessing_Day::getDataTableFromArray($this->interestTables['location_region']);
- $serialized = $tableRegion->getSerialized($maximumRows, $maximumRows, Piwik_Archive::INDEX_NB_VISITS);
- $archiveProcessing->insertBlobRecord(self::VISITS_BY_REGION_RECORD_NAME, $serialized);
- destroy($tableRegion);
-
- $tableCity = Piwik_ArchiveProcessing_Day::getDataTableFromArray($this->interestTables['location_city']);
- $this->setLatitudeLongitude($tableCity);
- $serialized = $tableCity->getSerialized($maximumRows, $maximumRows, Piwik_Archive::INDEX_NB_VISITS);
- $archiveProcessing->insertBlobRecord(self::VISITS_BY_CITY_RECORD_NAME, $serialized);
- destroy($tableCity);
- }
-
- /**
- * Makes sure the region and city of a query row are unique.
- *
- * @param array $row
- */
- private function setLongCityRegionId(&$row)
- {
- static $locationColumns = array('location_region', 'location_country', 'location_city');
-
- // to be on the safe side, remove the location separator from the region/city/country we
- // get from the query
- foreach ($locationColumns as $column) {
- $row[$column] = str_replace(self::LOCATION_SEPARATOR, '', $row[$column]);
- }
-
- if (!empty($row['location_region'])) // do not differentiate between unknown regions
- {
- $row['location_region'] = $row['location_region'] . self::LOCATION_SEPARATOR . $row['location_country'];
- }
-
- if (!empty($row['location_city'])) // do not differentiate between unknown cities
- {
- $row['location_city'] = $row['location_city'] . self::LOCATION_SEPARATOR . $row['location_region'];
- }
- }
/**
* Returns a list of country codes for a given continent code.
@@ -500,24 +333,4 @@ class Piwik_UserCountry extends Piwik_Plugin
'bind' => '-'); // HACK: SegmentExpression requires a $bind, even if there's nothing to bind
}
- /**
- * Utility method, appends latitude/longitude pairs to city table labels, if that data
- * exists for the city.
- */
- private function setLatitudeLongitude($tableCity)
- {
- foreach ($tableCity->getRows() as $row) {
- $label = $row->getColumn('label');
- if (isset($this->latLongForCities[$label])) {
- // get lat/long for city
- list($lat, $long) = $this->latLongForCities[$label];
- $lat = round($lat, Piwik_UserCountry_LocationProvider::GEOGRAPHIC_COORD_PRECISION);
- $long = round($long, Piwik_UserCountry_LocationProvider::GEOGRAPHIC_COORD_PRECISION);
-
- // set latitude + longitude metadata
- $row->setMetadata('lat', $lat);
- $row->setMetadata('long', $long);
- }
- }
- }
}
diff --git a/plugins/UserCountry/functions.php b/plugins/UserCountry/functions.php
index debe9166b2..9db8a85d78 100644
--- a/plugins/UserCountry/functions.php
+++ b/plugins/UserCountry/functions.php
@@ -92,7 +92,7 @@ function Piwik_UserCountry_getRegionName($label)
return Piwik_Translate('General_Unknown');
}
- list($regionCode, $countryCode) = explode(Piwik_UserCountry::LOCATION_SEPARATOR, $label);
+ list($regionCode, $countryCode) = explode(Piwik_UserCountry_Archiver::LOCATION_SEPARATOR, $label);
return Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes($countryCode, $regionCode);
}
@@ -114,7 +114,7 @@ function Piwik_UserCountry_getPrettyRegionName($label)
return Piwik_Translate('General_Unknown');
}
- list($regionCode, $countryCode) = explode(Piwik_UserCountry::LOCATION_SEPARATOR, $label);
+ list($regionCode, $countryCode) = explode(Piwik_UserCountry_Archiver::LOCATION_SEPARATOR, $label);
$result = Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes($countryCode, $regionCode);
if ($countryCode != Piwik_Tracker_Visit::UNKNOWN_CODE && $countryCode != '') {
@@ -143,7 +143,7 @@ function Piwik_UserCountry_getPrettyCityName($label)
}
// get city name, region code & country code
- $parts = explode(Piwik_UserCountry::LOCATION_SEPARATOR, $label);
+ $parts = explode(Piwik_UserCountry_Archiver::LOCATION_SEPARATOR, $label);
$cityName = $parts[0];
$regionCode = $parts[1];
$countryCode = $parts[2];
diff --git a/plugins/UserCountry/javascripts/userCountry.js b/plugins/UserCountry/javascripts/userCountry.js
index 68e9165e1c..953c6c0c49 100755
--- a/plugins/UserCountry/javascripts/userCountry.js
+++ b/plugins/UserCountry/javascripts/userCountry.js
@@ -68,7 +68,6 @@ $(document).ready(function () {
var data = {
module: 'UserCountry',
action: action,
- token_auth: piwik.token_auth,
'continue': cont ? 1 : 0
};
for (var k in extraData) {
@@ -190,4 +189,58 @@ $(document).ready(function () {
ajaxRequest.setCallback(updateGeoIPSuccess);
ajaxRequest.send(false);
});
+ });
+
+ $('body').on('click', '#update-geoip-links', function () {
+ $('#geoipdb-update-info-error').hide();
+
+ var currentDownloading = null,
+ updateGeoIPSuccess = function (response) {
+ if (response && response.error) {
+ $('#geoip-progressbar-container').hide();
+ $('#geoipdb-update-info-error').html(response.error).show();
+ }
+ else if (response && response.to_download) {
+ var continuing = currentDownloading == response.to_download;
+ currentDownloading = response.to_download;
+
+ // show progress bar w/ message
+ $('#geoip-updater-progressbar').progressbar('option', 'value', 1);
+ $('#geoip-updater-progressbar-label').html(response.to_download_label);
+ $('#geoip-progressbar-container').show();
+
+ // start/continue download
+ downloadNextChunk(
+ 'downloadMissingGeoIpDb', 'geoipdb-update-info', 'geoip-updater-progressbar',
+ continuing, {key: response.to_download}, updateGeoIPSuccess);
+ }
+ else {
+ $('#geoipdb-update-info-error').hide();
+ $('#geoip-updater-progressbar-label').html('');
+ $('#geoip-progressbar-container').hide();
+
+ // fade in/out Done message
+ $('#done-updating-updater').fadeIn(1000, function () {
+ setTimeout(function () {
+ $('#done-updating-updater').fadeOut(1000);
+ }, 3000);
+ });
+ }
+ };
+
+ // setup the auto-updater
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams({
+ period: $('#geoip-update-period-cell').find('>input:checked').val()
+ }, 'get');
+ ajaxRequest.addParams({
+ module: 'UserCountry',
+ action: 'updateGeoIPLinks',
+ loc_db: $('#geoip-location-db').val(),
+ isp_db: $('#geoip-isp-db').val(),
+ org_db: $('#geoip-org-db').val()
+ }, 'post');
+ ajaxRequest.setCallback(updateGeoIPSuccess);
+ ajaxRequest.send(false);
+ });
});
diff --git a/plugins/UserCountryMap/Controller.php b/plugins/UserCountryMap/Controller.php
index d6ef194faa..078c1745bf 100644
--- a/plugins/UserCountryMap/Controller.php
+++ b/plugins/UserCountryMap/Controller.php
@@ -66,18 +66,15 @@ class Piwik_UserCountryMap_Controller extends Piwik_Controller
'no_data' => Piwik_Translate('CoreHome_ThereIsNoDataForThisReport')
));
- // template for ajax requests
- $view->reqParamsJSON = Piwik_Common::json_encode(array(
- 'period' => $period,
- 'idSite' => $idSite,
- 'date' => $date,
- 'token_auth' => $token_auth,
- 'format' => 'json',
- 'segment' => Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', '')),
- 'showRawMetrics' => 1,
- 'enable_filter_excludelowpop' => 1,
- 'filter_excludelowpop_value' => -1
- ));
+ $view->reqParamsJSON = $this->getEnrichedRequest($params = array(
+ 'period' => $period,
+ 'idSite' => $idSite,
+ 'date' => $date,
+ 'token_auth' => $token_auth,
+ 'enable_filter_excludelowpop' => 1,
+ 'filter_excludelowpop_value' => -1
+ ));
+
$view->metrics = $config['metrics'] = $this->getMetrics($idSite, $period, $date, $token_auth);
$config['svgBasePath'] = 'plugins/UserCountryMap/svg/';
$config['mapCssPath'] = 'plugins/UserCountryMap/stylesheets/map.css';
@@ -138,19 +135,29 @@ class Piwik_UserCountryMap_Controller extends Piwik_Controller
'goal_conversions' => Piwik_Translate('UserCountryMap_GoalConversions'),
));
- $view->reqParamsJSON = json_encode(array(
+ $view->reqParamsJSON = $this->getEnrichedRequest(array(
'period' => 'range',
'idSite' => $idSite,
'date' => self::REAL_TIME_WINDOW,
'token_auth' => $token_auth,
- 'format' => 'json',
- 'segment' => Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', '')),
- 'showRawMetrics' => 1
));
echo $view->render();
}
+ private function getEnrichedRequest($params)
+ {
+ $params['format'] = 'json';
+ $params['showRawMetrics'] = 1;
+ $segment = Piwik_ViewDataTable::getRawSegmentFromRequest();
+ if(!empty($segment)) {
+ $params['segment'] = $segment;
+ }
+
+ return Piwik_Common::json_encode($params);
+ }
+
+
private function checkUserCountryPluginEnabled()
{
if (!Piwik_PluginsManager::getInstance()->isPluginActivated('UserCountry')) {
@@ -192,7 +199,7 @@ class Piwik_UserCountryMap_Controller extends Piwik_Controller
. "&period=" . $period
. "&date=" . $date
. "&token_auth=" . $token_auth
- . "&segment=" . Piwik_Common::unsanitizeInputValue(Piwik_Common::getRequestVar('segment', ''))
+ . "&segment=" . Piwik_ViewDataTable::getRawSegmentFromRequest()
. "&enable_filter_excludelowpop=1"
. "&showRawMetrics=1";
diff --git a/plugins/UserCountryMap/javascripts/realtime-map.js b/plugins/UserCountryMap/javascripts/realtime-map.js
index 431b749089..54d5cdfdc3 100644
--- a/plugins/UserCountryMap/javascripts/realtime-map.js
+++ b/plugins/UserCountryMap/javascripts/realtime-map.js
@@ -215,7 +215,7 @@
* that corresponds to a visit on the map
*/
function highlightVisit(r) {
- $('#visitsLive li#' + r.idVisit + ' .datetime')
+ $('#visitsLive').find('li#' + r.idVisit + ' .datetime')
.css('background', '#E4CD74');
}
@@ -224,7 +224,7 @@
* the visit marker on the map
*/
function unhighlightVisit(r) {
- $('#visitsLive li#' + r.idVisit + ' .datetime')
+ $('#visitsLive').find('li#' + r.idVisit + ' .datetime')
.css({ background: '#E4E2D7' });
}
diff --git a/plugins/UserCountryMap/javascripts/visitor-map.js b/plugins/UserCountryMap/javascripts/visitor-map.js
index 0cd41f0164..5231865583 100644
--- a/plugins/UserCountryMap/javascripts/visitor-map.js
+++ b/plugins/UserCountryMap/javascripts/visitor-map.js
@@ -1167,7 +1167,7 @@
resize: function () {
var ratio, w, h,
map = this.map,
- maxHeight = $(window).height() - (this.theWidget && this.theWidget.isMaximised ? 150 : 55);
+ maxHeight = $(window).height() - (this.theWidget && this.theWidget.isMaximised ? 150 : 79);
ratio = map.viewAB.width / map.viewAB.height;
w = map.container.width();
h = w / ratio;
diff --git a/plugins/UserCountryMap/stylesheets/visitor-map.css b/plugins/UserCountryMap/stylesheets/visitor-map.css
index cc3340d895..534db96fd5 100644
--- a/plugins/UserCountryMap/stylesheets/visitor-map.css
+++ b/plugins/UserCountryMap/stylesheets/visitor-map.css
@@ -13,7 +13,7 @@
top: 42% !important;
right: 10px !important;
left: 10px !important;
- z-index: 999 !important;
+ z-index: 990 !important;
display: block;
font-size: 12px;
color: #000;
diff --git a/plugins/UserSettings/API.php b/plugins/UserSettings/API.php
index e438b96b2f..1aa80230f9 100644
--- a/plugins/UserSettings/API.php
+++ b/plugins/UserSettings/API.php
@@ -37,7 +37,7 @@ class Piwik_UserSettings_API
Piwik::checkUserHasViewAccess($idSite);
$archive = Piwik_Archive::build($idSite, $period, $date, $segment);
$dataTable = $archive->getDataTable($name);
- $dataTable->filter('Sort', array(Piwik_Archive::INDEX_NB_VISITS));
+ $dataTable->filter('Sort', array(Piwik_Metrics::INDEX_NB_VISITS));
$dataTable->queueFilter('ReplaceColumnNames');
$dataTable->queueFilter('ReplaceSummaryRowLabel');
return $dataTable;
@@ -45,20 +45,20 @@ class Piwik_UserSettings_API
public function getResolution($idSite, $period, $date, $segment = false)
{
- $dataTable = $this->getDataTable('UserSettings_resolution', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserSettings_Archiver::RESOLUTION_RECORD_NAME, $idSite, $period, $date, $segment);
return $dataTable;
}
public function getConfiguration($idSite, $period, $date, $segment = false)
{
- $dataTable = $this->getDataTable('UserSettings_configuration', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserSettings_Archiver::CONFIGURATION_RECORD_NAME, $idSite, $period, $date, $segment);
$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getConfigurationLabel'));
return $dataTable;
}
public function getOS($idSite, $period, $date, $segment = false, $addShortLabel = true)
{
- $dataTable = $this->getDataTable('UserSettings_os', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserSettings_Archiver::OS_RECORD_NAME, $idSite, $period, $date, $segment);
// these filters are applied directly so other API methods can use GroupBy on the result of this method
$dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getOSLogo'));
if ($addShortLabel) {
@@ -88,18 +88,29 @@ class Piwik_UserSettings_API
{
$dataTable = $this->getOS($idSite, $period, $date, $segment, $addShortLabel = false);
$dataTable->filter('GroupBy', array('label', 'Piwik_UserSettings_getDeviceTypeFromOS'));
+ $this->ensureDefaultRowsInTable($dataTable);
- // make sure the datatable has a row for mobile & desktop (if it has rows)
- $dataTables = array($dataTable);
- if ($dataTable instanceof Piwik_DataTable_Array) {
- $dataTables = $dataTable->getArray();
- }
+ // set the logo metadata
+ $dataTable->queueFilter('MetadataCallbackReplace',
+ array('logo', 'Piwik_UserSettings_getDeviceTypeImg', null, array('label')));
+
+ // translate the labels
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_Translate'));
+ return $dataTable;
+ }
+
+ protected function ensureDefaultRowsInTable($dataTable)
+ {
$requiredRows = array(
- 'General_Desktop' => Piwik_Archive::INDEX_NB_VISITS,
- 'General_Mobile' => Piwik_Archive::INDEX_NB_VISITS
+ 'General_Desktop' => Piwik_Metrics::INDEX_NB_VISITS,
+ 'General_Mobile' => Piwik_Metrics::INDEX_NB_VISITS
);
+ $dataTables = array($dataTable);
+ if ($dataTable instanceof Piwik_DataTable_Array) {
+ $dataTables = $dataTable->getArray();
+ }
foreach ($dataTables AS $table) {
if ($table->getRowsCount() == 0) {
continue;
@@ -113,22 +124,19 @@ class Piwik_UserSettings_API
}
}
}
+ }
- // set the logo metadata
- $dataTable->queueFilter('MetadataCallbackReplace',
- array('logo', 'Piwik_UserSettings_getDeviceTypeImg', null, array('label')));
-
- // translate the labels
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_Translate'));
-
+ public function getBrowserVersion($idSite, $period, $date, $segment = false)
+ {
+ $dataTable = $this->getBrowserTable($idSite, $period, $date, $segment);
+ $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'Piwik_getBrowserShortLabel'));
return $dataTable;
}
- public function getBrowserVersion($idSite, $period, $date, $segment = false)
+ protected function getBrowserTable($idSite, $period, $date, $segment)
{
- $dataTable = $this->getDataTable('UserSettings_browser', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserSettings_Archiver::BROWSER_RECORD_NAME, $idSite, $period, $date, $segment);
$dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowsersLogo'));
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'Piwik_getBrowserShortLabel'));
$dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserLabel'));
return $dataTable;
}
@@ -139,19 +147,14 @@ class Piwik_UserSettings_API
*/
public function getBrowser($idSite, $period, $date, $segment = false)
{
- $dataTable = $this->getDataTable('UserSettings_browser', $idSite, $period, $date, $segment);
- $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getBrowsersLogo'));
- $dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserLabel'));
-
- $getBrowserFromBrowserVersion = 'Piwik_UserSettings_getBrowserFromBrowserVersion';
- $dataTable->filter('GroupBy', array('label', $getBrowserFromBrowserVersion));
-
+ $dataTable = $this->getBrowserTable($idSite, $period, $date, $segment);
+ $dataTable->filter('GroupBy', array('label', 'Piwik_UserSettings_getBrowserFromBrowserVersion'));
return $dataTable;
}
public function getBrowserType($idSite, $period, $date, $segment = false)
{
- $dataTable = $this->getDataTable('UserSettings_browserType', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserSettings_Archiver::BROWSER_TYPE_RECORD_NAME, $idSite, $period, $date, $segment);
$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'shortLabel', 'ucfirst'));
$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getBrowserTypeLabel'));
return $dataTable;
@@ -159,7 +162,7 @@ class Piwik_UserSettings_API
public function getWideScreen($idSite, $period, $date, $segment = false)
{
- $dataTable = $this->getDataTable('UserSettings_wideScreen', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserSettings_Archiver::SCREEN_TYPE_RECORD_NAME, $idSite, $period, $date, $segment);
$dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik_getScreensLogo'));
$dataTable->queueFilter('ColumnCallbackReplace', array('label', 'ucfirst'));
return $dataTable;
@@ -168,10 +171,10 @@ class Piwik_UserSettings_API
public function getPlugin($idSite, $period, $date, $segment = false)
{
// fetch all archive data required
- $dataTable = $this->getDataTable('UserSettings_plugin', $idSite, $period, $date, $segment);
- $browserTypes = $this->getDataTable('UserSettings_browserType', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserSettings_Archiver::PLUGIN_RECORD_NAME, $idSite, $period, $date, $segment);
+ $browserTypes = $this->getDataTable(Piwik_UserSettings_Archiver::BROWSER_TYPE_RECORD_NAME, $idSite, $period, $date, $segment);
$archive = Piwik_Archive::build($idSite, $period, $date, $segment);
- $visitsSums = $archive->getNumeric('nb_visits');
+ $visitsSums = $archive->getDataTableFromNumeric('nb_visits');
// check whether given tables are arrays
if ($dataTable instanceof Piwik_DataTable_Array) {
@@ -179,14 +182,13 @@ class Piwik_UserSettings_API
$browserTypesArray = $browserTypes->getArray();
$visitSumsArray = $visitsSums->getArray();
} else {
- $tableArray = Array($dataTable);
- $browserTypesArray = Array($browserTypes);
- $visitSumsArray = Array($visitsSums);
+ $tableArray = array($dataTable);
+ $browserTypesArray = array($browserTypes);
+ $visitSumsArray = array($visitsSums);
}
// walk through the results and calculate the percentage
foreach ($tableArray as $key => $table) {
-
// get according browserType table
foreach ($browserTypesArray AS $k => $browsers) {
if ($k == $key) {
@@ -198,7 +200,11 @@ class Piwik_UserSettings_API
foreach ($visitSumsArray AS $k => $visits) {
if ($k == $key) {
if (is_object($visits)) {
- $visitsSumTotal = (float)$visits->getFirstRow()->getColumn(0);
+ if ($visits->getRowsCount() == 0) {
+ $visitsSumTotal = 0;
+ } else {
+ $visitsSumTotal = (float)$visits->getFirstRow()->getColumn('nb_visits');
+ }
} else {
$visitsSumTotal = (float)$visits;
}
@@ -210,7 +216,7 @@ class Piwik_UserSettings_API
$ieStats = $browserType->getRowFromLabel('ie');
if ($ieStats !== false) {
- $ieVisits = $ieStats->getColumn(Piwik_Archive::INDEX_NB_VISITS);
+ $ieVisits = $ieStats->getColumn(Piwik_Metrics::INDEX_NB_VISITS);
}
$visitsSum = $visitsSumTotal - $ieVisits;
@@ -222,7 +228,7 @@ class Piwik_UserSettings_API
// The filter must be applied now so that the new column can
// be sorted by the generic filters (applied right after this loop exits)
- $table->filter('ColumnCallbackAddColumnPercentage', array('nb_visits_percentage', Piwik_Archive::INDEX_NB_VISITS, $visitsSum, 1));
+ $table->filter('ColumnCallbackAddColumnPercentage', array('nb_visits_percentage', Piwik_Metrics::INDEX_NB_VISITS, $visitsSum, 1));
$table->filter('RangeCheck', array('nb_visits_percentage'));
}
@@ -234,7 +240,7 @@ class Piwik_UserSettings_API
public function getLanguage($idSite, $period, $date, $segment = false)
{
- $dataTable = $this->getDataTable('UserSettings_language', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_UserSettings_Archiver::LANGUAGE_RECORD_NAME, $idSite, $period, $date, $segment);
$dataTable->filter('ColumnCallbackReplace', array('label', 'Piwik_LanguageTranslate'));
$dataTable->filter('ReplaceColumnNames');
return $dataTable;
diff --git a/plugins/UserSettings/Archiver.php b/plugins/UserSettings/Archiver.php
new file mode 100644
index 0000000000..d6ba9e32b2
--- /dev/null
+++ b/plugins/UserSettings/Archiver.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_UserSettings
+ */
+
+require_once PIWIK_INCLUDE_PATH . '/plugins/UserSettings/functions.php';
+
+class Piwik_UserSettings_Archiver extends Piwik_PluginsArchiver
+{
+ const LANGUAGE_RECORD_NAME = 'UserSettings_language';
+ const PLUGIN_RECORD_NAME = 'UserSettings_plugin';
+ const SCREEN_TYPE_RECORD_NAME = 'UserSettings_wideScreen';
+ const RESOLUTION_RECORD_NAME = 'UserSettings_resolution';
+ const BROWSER_RECORD_NAME = 'UserSettings_browser';
+ const BROWSER_TYPE_RECORD_NAME = 'UserSettings_browserType';
+ const OS_RECORD_NAME = 'UserSettings_os';
+ const CONFIGURATION_RECORD_NAME = 'UserSettings_configuration';
+
+ const LANGUAGE_DIMENSION = "log_visit.location_browser_lang";
+ const RESOLUTION_DIMENSION = "log_visit.config_resolution";
+ const BROWSER_VERSION_DIMENSION = "CONCAT(log_visit.config_browser_name, ';', log_visit.config_browser_version)";
+ const OS_DIMENSION = "log_visit.config_os";
+ const CONFIGURATION_DIMENSION = "CONCAT(log_visit.config_os, ';', log_visit.config_browser_name, ';', log_visit.config_resolution)";
+
+ public function archiveDay()
+ {
+ $this->aggregateByConfiguration();
+ $this->aggregateByOs();
+ $this->aggregateByBrowser();
+ $this->aggregateByResolutionAndScreenType();
+ $this->aggregateByPlugin();
+ $this->aggregateByLanguage();
+ }
+
+ protected function aggregateByConfiguration()
+ {
+ $metrics = $this->getProcessor()->getMetricsForDimension(self::CONFIGURATION_DIMENSION);
+ $table = $this->getProcessor()->getDataTableFromDataArray($metrics);
+ $this->insertTable(self::CONFIGURATION_RECORD_NAME, $table);
+ }
+
+ protected function aggregateByOs()
+ {
+ $metrics = $this->getProcessor()->getMetricsForDimension(self::OS_DIMENSION);
+ $table = $this->getProcessor()->getDataTableFromDataArray($metrics);
+ $this->insertTable(self::OS_RECORD_NAME, $table);
+ }
+
+ protected function aggregateByBrowser()
+ {
+ $tableBrowser = $this->aggregateByBrowserVersion();
+ $this->aggregateByBrowserType($tableBrowser);
+ }
+
+ protected function aggregateByBrowserVersion()
+ {
+ $metrics = $this->getProcessor()->getMetricsForDimension(self::BROWSER_VERSION_DIMENSION);
+ $tableBrowser = $this->getProcessor()->getDataTableFromDataArray($metrics);
+ $this->insertTable(self::BROWSER_RECORD_NAME, $tableBrowser);
+ return $tableBrowser;
+ }
+
+ protected function aggregateByBrowserType(Piwik_DataTable $tableBrowser)
+ {
+ $tableBrowser->filter('GroupBy', array('label', 'Piwik_getBrowserFamily'));
+ $this->insertTable(self::BROWSER_TYPE_RECORD_NAME, $tableBrowser);
+ }
+
+ protected function aggregateByResolutionAndScreenType()
+ {
+ $resolutions = $this->aggregateByResolution();
+ $this->aggregateByScreenType($resolutions);
+ }
+
+ protected function aggregateByResolution()
+ {
+ $metrics = $this->getProcessor()->getMetricsForDimension(self::RESOLUTION_DIMENSION);
+ $table = $this->getProcessor()->getDataTableFromDataArray($metrics);
+ $table->filter('ColumnCallbackDeleteRow', array('label', 'Piwik_UserSettings_keepStrlenGreater'));
+ $this->insertTable(self::RESOLUTION_RECORD_NAME, $table);
+ return $table;
+ }
+
+ protected function aggregateByScreenType(Piwik_DataTable $resolutions)
+ {
+ $resolutions->filter('GroupBy', array('label', 'Piwik_getScreenTypeFromResolution'));
+ $this->insertTable(self::SCREEN_TYPE_RECORD_NAME, $resolutions);
+ }
+
+ protected function aggregateByPlugin()
+ {
+ $selects = array(
+ "sum(case log_visit.config_pdf when 1 then 1 else 0 end) as pdf",
+ "sum(case log_visit.config_flash when 1 then 1 else 0 end) as flash",
+ "sum(case log_visit.config_java when 1 then 1 else 0 end) as java",
+ "sum(case log_visit.config_director when 1 then 1 else 0 end) as director",
+ "sum(case log_visit.config_quicktime when 1 then 1 else 0 end) as quicktime",
+ "sum(case log_visit.config_realplayer when 1 then 1 else 0 end) as realplayer",
+ "sum(case log_visit.config_windowsmedia when 1 then 1 else 0 end) as windowsmedia",
+ "sum(case log_visit.config_gears when 1 then 1 else 0 end) as gears",
+ "sum(case log_visit.config_silverlight when 1 then 1 else 0 end) as silverlight",
+ "sum(case log_visit.config_cookie when 1 then 1 else 0 end) as cookie"
+ );
+
+ $query = $this->getLogAggregator()->queryVisitsByDimension(array(), false, $selects, $metrics = array());
+ $data = $query->fetch();
+ $cleanRow = Piwik_DataAccess_LogAggregator::makeArrayOneColumn($data, Piwik_Metrics::INDEX_NB_VISITS);
+ $table = Piwik_DataTable::makeFromIndexedArray($cleanRow);
+ $this->insertTable(self::PLUGIN_RECORD_NAME, $table);
+ }
+
+ protected function aggregateByLanguage()
+ {
+ $query = $this->getLogAggregator()->queryVisitsByDimension( array("label" => self::LANGUAGE_DIMENSION) );
+ $languageCodes = array_keys(Piwik_Common::getLanguagesList());
+ $metricsByLanguage = new Piwik_DataArray();
+ while ($row = $query->fetch()) {
+ $code = Piwik_Common::extractLanguageCodeFromBrowserLanguage($row['label'], $languageCodes);
+ $metricsByLanguage->sumMetricsVisits($code, $row);
+ }
+
+ $tableLanguage = $this->getProcessor()->getDataTableFromDataArray($metricsByLanguage);
+ $this->insertTable(self::LANGUAGE_RECORD_NAME, $tableLanguage);
+ }
+
+ protected function insertTable($recordName, Piwik_DataTable $table)
+ {
+ return $this->getProcessor()->insertBlobRecord($recordName, $table->getSerialized($this->maximumRows, null, Piwik_Metrics::INDEX_NB_VISITS));
+ }
+
+ public function archivePeriod()
+ {
+ $dataTableToSum = array(
+ self::CONFIGURATION_RECORD_NAME,
+ self::OS_RECORD_NAME,
+ self::BROWSER_RECORD_NAME,
+ self::BROWSER_TYPE_RECORD_NAME,
+ self::RESOLUTION_RECORD_NAME,
+ self::SCREEN_TYPE_RECORD_NAME,
+ self::PLUGIN_RECORD_NAME,
+ self::LANGUAGE_RECORD_NAME,
+ );
+ $this->getProcessor()->aggregateDataTableReports($dataTableToSum, $this->maximumRows);
+ }
+}
+
diff --git a/plugins/UserSettings/UserSettings.php b/plugins/UserSettings/UserSettings.php
index 90be882806..19b5252360 100644
--- a/plugins/UserSettings/UserSettings.php
+++ b/plugins/UserSettings/UserSettings.php
@@ -232,7 +232,7 @@ class Piwik_UserSettings extends Piwik_Plugin
if (empty($segment)) continue;
$segments[] = array(
'type' => 'dimension',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => $columnName,
'segment' => $segment,
'acceptedValues' => $acceptedValues,
@@ -269,70 +269,18 @@ class Piwik_UserSettings extends Piwik_Plugin
* are superset of an existing report (eg. Browser family is built from the Browser report)
*
* @param Piwik_Event_Notification $notification notification object
- * @return void
*/
function archiveDay($notification)
{
- require_once PIWIK_INCLUDE_PATH . '/plugins/UserSettings/functions.php';
- $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
- $columnToSortByBeforeTruncation = Piwik_Archive::INDEX_NB_VISITS;
-
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveProcessing = $archiveProcessing;
-
- $recordName = 'UserSettings_configuration';
- $labelSQL = "CONCAT(log_visit.config_os, ';', log_visit.config_browser_name, ';', log_visit.config_resolution)";
- $interestByConfiguration = $archiveProcessing->getArrayInterestForLabel($labelSQL);
-
- $tableConfiguration = $archiveProcessing->getDataTableFromArray($interestByConfiguration);
- $archiveProcessing->insertBlobRecord($recordName, $tableConfiguration->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
- destroy($tableConfiguration);
-
- $recordName = 'UserSettings_os';
- $labelSQL = "log_visit.config_os";
- $interestByOs = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- $tableOs = $archiveProcessing->getDataTableFromArray($interestByOs);
- $archiveProcessing->insertBlobRecord($recordName, $tableOs->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
- destroy($tableOs);
+ $archiveProcessor = $notification->getNotificationObject();
- $recordName = 'UserSettings_browser';
- $labelSQL = "CONCAT(log_visit.config_browser_name, ';', log_visit.config_browser_version)";
- $interestByBrowser = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- $tableBrowser = $archiveProcessing->getDataTableFromArray($interestByBrowser);
- $archiveProcessing->insertBlobRecord($recordName, $tableBrowser->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
-
- $recordName = 'UserSettings_browserType';
- $tableBrowserType = $this->getTableBrowserByType($tableBrowser);
- $archiveProcessing->insertBlobRecord($recordName, $tableBrowserType->getSerialized());
- destroy($tableBrowser);
- destroy($tableBrowserType);
-
- $recordName = 'UserSettings_resolution';
- $labelSQL = "log_visit.config_resolution";
- $interestByResolution = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- $tableResolution = $archiveProcessing->getDataTableFromArray($interestByResolution);
- $tableResolution->filter('ColumnCallbackDeleteRow', array('label', 'Piwik_UserSettings_keepStrlenGreater'));
- $archiveProcessing->insertBlobRecord($recordName, $tableResolution->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
-
- $recordName = 'UserSettings_wideScreen';
- $tableWideScreen = $this->getTableWideScreen($tableResolution);
- $archiveProcessing->insertBlobRecord($recordName, $tableWideScreen->getSerialized());
- destroy($tableResolution);
- destroy($tableWideScreen);
-
- $recordName = 'UserSettings_plugin';
- $tablePlugin = $this->getDataTablePlugin();
- $archiveProcessing->insertBlobRecord($recordName, $tablePlugin->getSerialized());
- destroy($tablePlugin);
-
- $recordName = 'UserSettings_language';
- $tableLanguage = $this->getDataTableLanguages();
- $archiveProcessing->insertBlobRecord($recordName, $tableLanguage->getSerialized($maximumRowsInDataTable, null, $columnToSortByBeforeTruncation));
+ $archiving = new Piwik_UserSettings_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archiveDay();
+ }
}
+
/**
* Period archiving: simply sums up daily archives
*
@@ -341,121 +289,11 @@ class Piwik_UserSettings extends Piwik_Plugin
*/
function archivePeriod($notification)
{
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $maximumRowsInDataTable = Piwik_Config::getInstance()->General['datatable_archiving_maximum_rows_standard'];
-
- $dataTableToSum = array(
- 'UserSettings_configuration',
- 'UserSettings_os',
- 'UserSettings_browser',
- 'UserSettings_browserType',
- 'UserSettings_resolution',
- 'UserSettings_wideScreen',
- 'UserSettings_plugin',
- 'UserSettings_language',
- );
-
- $archiveProcessing->archiveDataTable($dataTableToSum, null, $maximumRowsInDataTable);
- }
+ $archiveProcessor = $notification->getNotificationObject();
- /**
- * Returns the report Visits by Screen type given the Resolution table
- *
- * @param Piwik_DataTable $tableResolution
- * @return Piwik_DataTable
- */
- protected function getTableWideScreen(Piwik_DataTable $tableResolution)
- {
- $nameToRow = array();
- foreach ($tableResolution->getRows() as $row) {
- $resolution = $row->getColumn('label');
- $name = Piwik_getScreenTypeFromResolution($resolution);
- if (!isset($nameToRow[$name])) {
- $nameToRow[$name] = new Piwik_DataTable_Row();
- $nameToRow[$name]->addColumn('label', $name);
- }
-
- $nameToRow[$name]->sumRow($row);
- }
- $tableWideScreen = new Piwik_DataTable();
- $tableWideScreen->addRowsFromArray($nameToRow);
-
- return $tableWideScreen;
- }
-
- /**
- * Returns the report Visits by Browser family given the Browser report
- *
- * @param Piwik_DataTable $tableBrowser
- * @return Piwik_DataTable
- */
- protected function getTableBrowserByType(Piwik_DataTable $tableBrowser)
- {
- $nameToRow = array();
- foreach ($tableBrowser->getRows() as $row) {
- $browserLabel = $row->getColumn('label');
- $familyNameToUse = Piwik_getBrowserFamily($browserLabel);
- if (!isset($nameToRow[$familyNameToUse])) {
- $nameToRow[$familyNameToUse] = new Piwik_DataTable_Row();
- $nameToRow[$familyNameToUse]->addColumn('label', $familyNameToUse);
- }
- $nameToRow[$familyNameToUse]->sumRow($row);
- }
-
- $tableBrowserType = new Piwik_DataTable();
- $tableBrowserType->addRowsFromArray($nameToRow);
- return $tableBrowserType;
- }
-
- /**
- * Returns SQL that processes stats for Plugins
- *
- * @return Piwik_DataTable_Simple
- */
- protected function getDataTablePlugin()
- {
- $toSelect = "sum(case log_visit.config_pdf when 1 then 1 else 0 end) as pdf,
- sum(case log_visit.config_flash when 1 then 1 else 0 end) as flash,
- sum(case log_visit.config_java when 1 then 1 else 0 end) as java,
- sum(case log_visit.config_director when 1 then 1 else 0 end) as director,
- sum(case log_visit.config_quicktime when 1 then 1 else 0 end) as quicktime,
- sum(case log_visit.config_realplayer when 1 then 1 else 0 end) as realplayer,
- sum(case log_visit.config_windowsmedia when 1 then 1 else 0 end) as windowsmedia,
- sum(case log_visit.config_gears when 1 then 1 else 0 end) as gears,
- sum(case log_visit.config_silverlight when 1 then 1 else 0 end) as silverlight,
- sum(case log_visit.config_cookie when 1 then 1 else 0 end) as cookie ";
- return $this->archiveProcessing->getSimpleDataTableFromSelect($toSelect, Piwik_Archive::INDEX_NB_VISITS);
- }
-
- protected function getDataTableLanguages()
- {
- $labelSQL = "log_visit.location_browser_lang";
- $interestByLanguage = $this->archiveProcessing->getArrayInterestForLabel($labelSQL);
-
- $languageCodes = array_keys(Piwik_Common::getLanguagesList());
-
- foreach ($interestByLanguage as $lang => $count) {
- // get clean language code
- $code = Piwik_Common::extractLanguageCodeFromBrowserLanguage($lang, $languageCodes);
- if ($code != $lang) {
- if (!array_key_exists($code, $interestByLanguage)) {
- $interestByLanguage[$code] = array();
- }
- // Add the values to the primary language
- foreach ($count as $key => $value) {
- if (array_key_exists($key, $interestByLanguage[$code])) {
- $interestByLanguage[$code][$key] += $value;
- } else {
- $interestByLanguage[$code][$key] = $value;
- }
- }
- unset($interestByLanguage[$lang]);
- }
+ $archiving = new Piwik_UserSettings_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
}
- $tableLanguage = $this->archiveProcessing->getDataTableFromArray($interestByLanguage);
- return $tableLanguage;
}
}
diff --git a/plugins/UserSettings/functions.php b/plugins/UserSettings/functions.php
index e1c5579608..80a63a589e 100644
--- a/plugins/UserSettings/functions.php
+++ b/plugins/UserSettings/functions.php
@@ -153,6 +153,16 @@ function Piwik_getBrowserVersion($str)
return substr($str, strpos($str, ';') + 1);
}
+function Piwik_getLogoImageFromId($dir, $id)
+{
+ $path = $dir.'/'.$id.'.gif';
+ if (file_exists(PIWIK_INCLUDE_PATH . '/' . $path)) {
+ return $path;
+ } else {
+ return $dir.'/UNK.gif';
+ }
+}
+
function Piwik_getBrowsersLogo($label)
{
$id = Piwik_getBrowserId($label);
@@ -160,7 +170,7 @@ function Piwik_getBrowsersLogo($label)
if (empty($id)) {
$id = 'UNK';
}
- return 'plugins/UserSettings/images/browsers/' . $id . '.gif';
+ return Piwik_getLogoImageFromId('plugins/UserSettings/images/browsers', $id);
}
function Piwik_getOSLogo($label)
@@ -169,8 +179,7 @@ function Piwik_getOSLogo($label)
if (empty($label)) {
$label = 'UNK';
}
- $path = 'plugins/UserSettings/images/os/' . $label . '.gif';
- return $path;
+ return Piwik_getLogoImageFromId('plugins/UserSettings/images/os', $label);
}
function Piwik_getScreensLogo($label)
diff --git a/plugins/UserSettings/images/browsers/CM.gif b/plugins/UserSettings/images/browsers/CM.gif
new file mode 100644
index 0000000000..793e0fad3b
--- /dev/null
+++ b/plugins/UserSettings/images/browsers/CM.gif
Binary files differ
diff --git a/plugins/UserSettings/images/browsers/NB.gif b/plugins/UserSettings/images/browsers/NB.gif
new file mode 100644
index 0000000000..3d87e9f4bc
--- /dev/null
+++ b/plugins/UserSettings/images/browsers/NB.gif
Binary files differ
diff --git a/plugins/UserSettings/images/browsers/UN.gif b/plugins/UserSettings/images/browsers/UN.gif
new file mode 100644
index 0000000000..2c44083422
--- /dev/null
+++ b/plugins/UserSettings/images/browsers/UN.gif
Binary files differ
diff --git a/plugins/UserSettings/images/os/COS.gif b/plugins/UserSettings/images/os/COS.gif
new file mode 100644
index 0000000000..793e0fad3b
--- /dev/null
+++ b/plugins/UserSettings/images/os/COS.gif
Binary files differ
diff --git a/plugins/UserSettings/images/os/GTV.gif b/plugins/UserSettings/images/os/GTV.gif
new file mode 100644
index 0000000000..bdc4aefd88
--- /dev/null
+++ b/plugins/UserSettings/images/os/GTV.gif
Binary files differ
diff --git a/plugins/UserSettings/images/os/IOS.gif b/plugins/UserSettings/images/os/IOS.gif
new file mode 100644
index 0000000000..a3b970ae1b
--- /dev/null
+++ b/plugins/UserSettings/images/os/IOS.gif
Binary files differ
diff --git a/plugins/UserSettings/images/os/WIN.gif b/plugins/UserSettings/images/os/WIN.gif
new file mode 100644
index 0000000000..486f78064a
--- /dev/null
+++ b/plugins/UserSettings/images/os/WIN.gif
Binary files differ
diff --git a/plugins/UsersManager/javascripts/usersManager.js b/plugins/UsersManager/javascripts/usersManager.js
index 0db663d7c2..c24cbf73e6 100644
--- a/plugins/UsersManager/javascripts/usersManager.js
+++ b/plugins/UsersManager/javascripts/usersManager.js
@@ -196,7 +196,7 @@ $(document).ready(function () {
piwikHelper.hideAjaxError();
var idRow = $(this).attr('id');
var loginToDelete = $(this).parent().parent().find('#userLogin').html();
- $('#confirmUserRemove h2').text(sprintf(_pk_translate('UsersManager_DeleteConfirm_js'), '"' + loginToDelete + '"'));
+ $('#confirmUserRemove').find('h2').text(sprintf(_pk_translate('UsersManager_DeleteConfirm_js'), '"' + loginToDelete + '"'));
piwikHelper.modalConfirm('#confirmUserRemove', {yes: function () { sendDeleteUserAJAX(loginToDelete); }});
}
);
diff --git a/plugins/UsersManager/javascripts/usersSettings.js b/plugins/UsersManager/javascripts/usersSettings.js
index 0bfdf3d8bb..5708ca7a72 100644
--- a/plugins/UsersManager/javascripts/usersSettings.js
+++ b/plugins/UsersManager/javascripts/usersSettings.js
@@ -23,7 +23,7 @@ function sendUserSettingsAJAX() {
var passwordBis = $('#passwordBis').val();
var defaultReport = $('input[name=defaultReport]:checked').val();
if (defaultReport == 1) {
- defaultReport = $('#defaultReportSiteSelector .custom_select_main_link').attr('siteid');
+ defaultReport = $('#defaultReportSiteSelector').find('.custom_select_main_link').attr('siteid');
}
var postParams = {};
postParams.alias = alias;
@@ -52,7 +52,7 @@ function sendUserSettingsAJAX() {
function sendAnonymousUserSettingsAJAX() {
var anonymousDefaultReport = $('input[name=anonymousDefaultReport]:checked').val();
if (anonymousDefaultReport == 1) {
- anonymousDefaultReport = $('#anonymousDefaultReportWebsite option:selected').val();
+ anonymousDefaultReport = $('#anonymousDefaultReportWebsite').find('option:selected').val();
}
var anonymousDefaultDate = $('input[name=anonymousDefaultDate]:checked').val();
@@ -81,7 +81,7 @@ $(document).ready(function () {
}
});
- $('#userSettingsTable input').keypress(function (e) {
+ $('#userSettingsTable').find('input').keypress(function (e) {
var key = e.keyCode || e.which;
if (key == 13) {
$('#userSettingsSubmit').click();
diff --git a/plugins/VisitFrequency/API.php b/plugins/VisitFrequency/API.php
index d712b67831..986489f8eb 100644
--- a/plugins/VisitFrequency/API.php
+++ b/plugins/VisitFrequency/API.php
@@ -15,8 +15,10 @@
*/
class Piwik_VisitFrequency_API
{
- static private $instance = null;
+ const RETURNING_VISITOR_SEGMENT = "visitorType==returning";
+ const COLUMN_SUFFIX = "_returning";
+ static private $instance = null;
static public function getInstance()
{
if (self::$instance == null) {
@@ -27,113 +29,48 @@ class Piwik_VisitFrequency_API
public function get($idSite, $period, $date, $segment = false, $columns = false)
{
- Piwik::checkUserHasViewAccess($idSite);
- $archive = Piwik_Archive::build($idSite, $period, $date, $segment);
-
- // array values are comma separated
- $columns = Piwik::getArrayFromApiParameter($columns);
- $tempColumns = array();
-
- $bounceRateReturningRequested = $averageVisitDurationReturningRequested = $actionsPerVisitReturningRequested = false;
- if (!empty($columns)) {
- // make sure base metrics are there for processed metrics
- if (false !== ($bounceRateReturningRequested = array_search('bounce_rate_returning', $columns))) {
- if (!in_array('nb_visits_returning', $columns)) $tempColumns[] = 'nb_visits_returning';
- if (!in_array('bounce_count_returning', $columns)) $tempColumns[] = 'bounce_count_returning';
- unset($columns[$bounceRateReturningRequested]);
- }
- if (false !== ($actionsPerVisitReturningRequested = array_search('nb_actions_per_visit_returning', $columns))) {
- if (!in_array('nb_actions_returning', $columns)) $tempColumns[] = 'nb_actions_returning';
- if (!in_array('nb_visits_returning', $columns)) $tempColumns[] = 'nb_visits_returning';
- unset($columns[$actionsPerVisitReturningRequested]);
- }
- if (false !== ($averageVisitDurationReturningRequested = array_search('avg_time_on_site_returning', $columns))) {
- if (!in_array('sum_visit_length_returning', $columns)) $tempColumns[] = 'sum_visit_length_returning';
- if (!in_array('nb_visits_returning', $columns)) $tempColumns[] = 'nb_visits_returning';
- unset($columns[$averageVisitDurationReturningRequested]);
- }
-
- $tempColumns = array_unique($tempColumns);
- $columns = array_merge($columns, $tempColumns);
- } else {
- $bounceRateReturningRequested = $averageVisitDurationReturningRequested = $actionsPerVisitReturningRequested = true;
- $columns = array(
- 'nb_visits_returning',
- 'nb_actions_returning',
- 'max_actions_returning',
- 'sum_visit_length_returning',
- 'bounce_count_returning',
- 'nb_visits_converted_returning',
- );
-
- if ($period == 'day') {
- $columns = array_merge(array('nb_uniq_visitors_returning'), $columns);
- }
- }
- $dataTable = $archive->getDataTableFromNumeric($columns);
-
- // Process ratio metrics
- if ($bounceRateReturningRequested !== false) {
- $dataTable->filter('ColumnCallbackAddColumnPercentage', array('bounce_rate_returning', 'bounce_count_returning', 'nb_visits_returning', 0));
- }
- if ($actionsPerVisitReturningRequested !== false) {
- $dataTable->filter('ColumnCallbackAddColumnQuotient', array('nb_actions_per_visit_returning', 'nb_actions_returning', 'nb_visits_returning', 1));
- }
- if ($averageVisitDurationReturningRequested !== false) {
- $dataTable->filter('ColumnCallbackAddColumnQuotient', array('avg_time_on_site_returning', 'sum_visit_length_returning', 'nb_visits_returning', 0));
- }
-
- // remove temporary metrics that were used to compute processed metrics
- $dataTable->deleteColumns($tempColumns);
-
- return $dataTable;
+ $segment = $this->appendReturningVisitorSegment($segment);
+
+ $this->unprefixColumns($columns);
+ $params = array(
+ 'idSite' => $idSite,
+ 'period' => $period,
+ 'date' => $date,
+ 'segment' => $segment,
+ 'columns' => implode(',', $columns),
+ 'format' => 'original',
+ 'serialize' => 0 // tests set this to 1
+ );
+ $table = Piwik_API_Request::processRequest('VisitsSummary.get', $params);
+ $this->prefixColumns($table, $period);
+ return $table;
}
- protected function getNumeric($idSite, $period, $date, $toFetch)
+ protected function appendReturningVisitorSegment($segment)
{
- Piwik::checkUserHasViewAccess($idSite);
- $archive = Piwik_Archive::build($idSite, $period, $date);
- $dataTable = $archive->getNumeric($toFetch);
- return $dataTable;
- }
-
- /**
- * @ignore
- */
- public function getVisitsReturning($idSite, $period, $date)
- {
- return $this->getNumeric($idSite, $period, $date, 'nb_visits_returning');
- }
-
- /**
- * @ignore
- */
- public function getActionsReturning($idSite, $period, $date)
- {
- return $this->getNumeric($idSite, $period, $date, 'nb_actions_returning');
- }
-
- /**
- * @ignore
- */
- public function getSumVisitsLengthReturning($idSite, $period, $date)
- {
- return $this->getNumeric($idSite, $period, $date, 'sum_visit_length_returning');
+ if (empty($segment)) {
+ $segment = '';
+ } else {
+ $segment .= Piwik_SegmentExpression::AND_DELIMITER;
+ }
+ $segment .= self::RETURNING_VISITOR_SEGMENT;
+ return $segment;
}
- /**
- * @ignore
- */
- public function getBounceCountReturning($idSite, $period, $date)
+ protected function unprefixColumns(&$columns)
{
- return $this->getNumeric($idSite, $period, $date, 'bounce_count_returning');
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ foreach ($columns as &$column) {
+ $column = str_replace(self::COLUMN_SUFFIX, "", $column);
+ }
}
- /**
- * @ignore
- */
- public function getConvertedVisitsReturning($idSite, $period, $date)
+ protected function prefixColumns($table, $period)
{
- return $this->getNumeric($idSite, $period, $date, 'nb_visits_converted_returning');
+ $rename = array();
+ foreach (Piwik_VisitsSummary_API::getInstance()->getColumns($period) as $oldColumn) {
+ $rename[$oldColumn] = $oldColumn . self::COLUMN_SUFFIX;
+ }
+ $table->filter('ReplaceColumnNames', array($rename));
}
}
diff --git a/plugins/VisitFrequency/VisitFrequency.php b/plugins/VisitFrequency/VisitFrequency.php
index 4659feee91..717e3f8eae 100644
--- a/plugins/VisitFrequency/VisitFrequency.php
+++ b/plugins/VisitFrequency/VisitFrequency.php
@@ -29,8 +29,6 @@ class Piwik_VisitFrequency extends Piwik_Plugin
function getListHooksRegistered()
{
$hooks = array(
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
'WidgetsList.add' => 'addWidgets',
'Menu.add' => 'addMenu',
'API.getReportMetadata' => 'getReportMetadata',
@@ -77,73 +75,5 @@ class Piwik_VisitFrequency extends Piwik_Plugin
{
Piwik_AddMenu('General_Visitors', 'VisitFrequency_SubmenuFrequency', array('module' => 'VisitFrequency', 'action' => 'index'));
}
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archivePeriod($notification)
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $numericToSum = array(
- 'nb_visits_returning',
- 'nb_actions_returning',
- 'sum_visit_length_returning',
- 'bounce_count_returning',
- 'nb_visits_converted_returning',
- );
- $archiveProcessing->archiveNumericValuesSum($numericToSum);
- $archiveProcessing->archiveNumericValuesMax('max_actions_returning');
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- function archiveDay($notification)
- {
- /* @var $archiveProcessing Piwik_ArchiveProcessing */
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $select = "count(distinct log_visit.idvisitor) as nb_uniq_visitors_returning,
- count(*) as nb_visits_returning,
- sum(log_visit.visit_total_actions) as nb_actions_returning,
- max(log_visit.visit_total_actions) as max_actions_returning,
- sum(log_visit.visit_total_time) as sum_visit_length_returning,
- sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as bounce_count_returning,
- sum(case log_visit.visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted_returning";
-
- $from = "log_visit";
-
- $where = "log_visit.visit_last_action_time >= ?
- AND log_visit.visit_last_action_time <= ?
- AND log_visit.idsite = ?
- AND log_visit.visitor_returning >= 1";
-
- $bind = array($archiveProcessing->getStartDatetimeUTC(),
- $archiveProcessing->getEndDatetimeUTC(), $archiveProcessing->idsite);
-
- $query = $archiveProcessing->getSegment()->getSelectQuery($select, $from, $where, $bind);
-
- $row = $archiveProcessing->db->fetchRow($query['sql'], $query['bind']);
-
- if ($row === false || $row === null) {
- $row['nb_visits_returning'] = 0;
- $row['nb_actions_returning'] = 0;
- $row['max_actions_returning'] = 0;
- $row['sum_visit_length_returning'] = 0;
- $row['bounce_count_returning'] = 0;
- $row['nb_visits_converted_returning'] = 0;
- }
-
- foreach ($row as $name => $value) {
- $archiveProcessing->insertNumericRecord($name, $value);
- }
- }
}
diff --git a/plugins/VisitTime/API.php b/plugins/VisitTime/API.php
index 3da0646fb7..2f4aaee06b 100644
--- a/plugins/VisitTime/API.php
+++ b/plugins/VisitTime/API.php
@@ -39,12 +39,12 @@ class Piwik_VisitTime_API
public function getVisitInformationPerLocalTime($idSite, $period, $date, $segment = false)
{
- return $this->getDataTable('VisitTime_localTime', $idSite, $period, $date, $segment);
+ return $this->getDataTable(Piwik_VisitTime_Archiver::LOCAL_TIME_RECORD_NAME, $idSite, $period, $date, $segment);
}
public function getVisitInformationPerServerTime($idSite, $period, $date, $segment = false, $hideFutureHoursWhenToday = false)
{
- $table = $this->getDataTable('VisitTime_serverTime', $idSite, $period, $date, $segment);
+ $table = $this->getDataTable(Piwik_VisitTime_Archiver::SERVER_TIME_RECORD_NAME, $idSite, $period, $date, $segment);
if ($hideFutureHoursWhenToday) {
$table = $this->removeHoursInFuture($table, $idSite, $period, $date);
}
@@ -62,26 +62,28 @@ class Piwik_VisitTime_API
*/
public function getByDayOfWeek($idSite, $period, $date, $segment = false)
{
- Piwik::checkUserHasViewAccess($idSite);
- // disabled for multiple sites/dates
- if (Piwik_Archive::isMultipleSites($idSite)) {
- throw new Exception("VisitTime.getByDayOfWeek does not support multiple sites.");
- }
+ Piwik::checkUserHasViewAccess($idSite);
- if (Piwik_Archive::isMultiplePeriod($date, $period)) {
+ // metrics to query
+ $metrics = Piwik_Metrics::getVisitsMetricNames();
+ unset($metrics[Piwik_Metrics::INDEX_MAX_ACTIONS]);
+
+ // disabled for multiple dates
+ if (Piwik_Period::isMultiplePeriod($date, $period)) {
throw new Exception("VisitTime.getByDayOfWeek does not support multiple dates.");
}
- // metrics to query
- $metrics = Piwik_ArchiveProcessing::getCoreMetrics();
-
// get metric data for every day within the supplied period
- $oSite = new Piwik_Site($idSite);
- $oPeriod = Piwik_Archive::makePeriodFromQueryParams($oSite, $period, $date);
+ $oPeriod = Piwik_Period::makePeriodFromQueryParams(Piwik_Site::getTimezoneFor($idSite), $period, $date);
$dateRange = $oPeriod->getDateStart()->toString() . ',' . $oPeriod->getDateEnd()->toString();
-
$archive = Piwik_Archive::build($idSite, 'day', $dateRange, $segment);
+
+ // disabled for multiple sites
+ if (count($archive->getParams()->getIdSites()) > 1) {
+ throw new Exception("VisitTime.getByDayOfWeek does not support multiple sites.");
+ }
+
$dataTable = $archive->getDataTableFromNumeric($metrics)->mergeChildren();
// if there's no data for this report, don't bother w/ anything else
@@ -97,7 +99,6 @@ class Piwik_VisitTime_API
foreach (array(1, 2, 3, 4, 5, 6, 7) as $day) {
$rows[] = array('label' => $day, 'nb_visits' => 0);
}
-
$result = new Piwik_DataTable();
$result->addRowsFromSimpleArray($rows);
$result->addDataTable($dataTable);
diff --git a/plugins/VisitTime/Archiver.php b/plugins/VisitTime/Archiver.php
new file mode 100644
index 0000000000..b695ba4558
--- /dev/null
+++ b/plugins/VisitTime/Archiver.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_VisitTime
+ */
+
+class Piwik_VisitTime_Archiver extends Piwik_PluginsArchiver
+{
+ const SERVER_TIME_RECORD_NAME = 'VisitTime_serverTime';
+ const LOCAL_TIME_RECORD_NAME = 'VisitTime_localTime';
+
+ public function archiveDay()
+ {
+ $this->aggregateByLocalTime();
+ $this->aggregateByServerTime();
+ }
+
+ protected function aggregateByServerTime()
+ {
+ $array = $this->getProcessor()->getMetricsForDimension( array("label" => "HOUR(log_visit.visit_last_action_time)" )) ;
+ $query = $this->getLogAggregator()->queryConversionsByDimension( array("label" => "HOUR(log_conversion.server_time)") );
+ if ($query === false) {
+ return;
+ }
+
+ while ($row = $query->fetch()) {
+ $array->sumMetricsGoals($row['label'], $row);
+ }
+ $array->enrichMetricsWithConversions();
+ $array = $this->convertTimeToLocalTimezone($array);
+ $this->ensureAllHoursAreSet($array);
+ $this->getProcessor()->insertBlobRecord(self::SERVER_TIME_RECORD_NAME, $this->getProcessor()->getDataTableFromDataArray($array)->getSerialized());
+ }
+
+ protected function aggregateByLocalTime()
+ {
+ $array = $this->getProcessor()->getMetricsForDimension("HOUR(log_visit.visitor_localtime)");
+ $this->ensureAllHoursAreSet($array);
+ $this->getProcessor()->insertBlobRecord(self::LOCAL_TIME_RECORD_NAME, $this->getProcessor()->getDataTableFromDataArray($array)->getSerialized());
+ }
+
+ protected function convertTimeToLocalTimezone(Piwik_DataArray &$array)
+ {
+ $date = Piwik_Date::factory($this->getProcessor()->getDateStart()->getDateStartUTC())->toString();
+ $timezone = $this->getProcessor()->getSite()->getTimezone();
+
+ $converted = array();
+ foreach ($array->getDataArray() as $hour => $stats) {
+ $datetime = $date . ' ' . $hour . ':00:00';
+ $hourInTz = (int)Piwik_Date::factory($datetime, $timezone)->toString('H');
+ $converted[$hourInTz] = $stats;
+ }
+ return new Piwik_DataArray($converted);
+ }
+
+
+ private function ensureAllHoursAreSet( Piwik_DataArray &$array)
+ {
+ $data = $array->getDataArray();
+ for ($i = 0; $i <= 23; $i++) {
+ if (empty($data[$i])) {
+ $array->sumMetricsVisits( $i, Piwik_DataArray::makeEmptyRow());
+ }
+ }
+ }
+
+ public function archivePeriod()
+ {
+ $dataTableToSum = array(
+ self::LOCAL_TIME_RECORD_NAME,
+ self::SERVER_TIME_RECORD_NAME,
+ );
+ $this->getProcessor()->aggregateDataTableReports($dataTableToSum);
+ }
+} \ No newline at end of file
diff --git a/plugins/VisitTime/Controller.php b/plugins/VisitTime/Controller.php
index 76e758270f..769c102761 100644
--- a/plugins/VisitTime/Controller.php
+++ b/plugins/VisitTime/Controller.php
@@ -58,15 +58,22 @@ class Piwik_VisitTime_Controller extends Piwik_Controller
if ($view instanceof Piwik_ViewDataTable_GenerateGraphHTML) {
$view->showAllTicks();
}
+ $dateRange = $this->getRangeDate();
+ $view->setFooterMessage(Piwik_Translate('General_ReportGeneratedFrom', $dateRange));
+
+ return $this->renderView($view, $fetch);
+ }
+
+ protected function getRangeDate()
+ {
// get query params
- $idsite = Piwik_Common::getRequestVar('idSite');
+ $idSite = Piwik_Common::getRequestVar('idSite');
$date = Piwik_Common::getRequestVar('date');
$period = Piwik_Common::getRequestVar('period');
// create a period instance
- $oSite = new Piwik_Site($idsite);
- $oPeriod = Piwik_Archive::makePeriodFromQueryParams($oSite, $period, $date);
+ $oPeriod = Piwik_Period::makePeriodFromQueryParams(Piwik_Site::getTimezoneFor($idSite), $period, $date);
// set the footer message using the period start & end date
$start = $oPeriod->getDateStart()->toString();
@@ -76,10 +83,7 @@ class Piwik_VisitTime_Controller extends Piwik_Controller
} else {
$dateRange = $start . " &ndash; " . $end;
}
-
- $view->setFooterMessage(Piwik_Translate('General_ReportGeneratedFrom', $dateRange));
-
- return $this->renderView($view, $fetch);
+ return $dateRange;
}
private function getGraph($controllerMethod, $apiMethod, $labelTranslation, $limit = 24)
diff --git a/plugins/VisitTime/VisitTime.php b/plugins/VisitTime/VisitTime.php
index 5b05342bed..b74a48f59e 100644
--- a/plugins/VisitTime/VisitTime.php
+++ b/plugins/VisitTime/VisitTime.php
@@ -114,7 +114,7 @@ class Piwik_VisitTime extends Piwik_Plugin
$acceptedValues = "0, 1, 2, 3, ..., 20, 21, 22, 23";
$segments[] = array(
'type' => 'dimension',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => Piwik_Translate('VisitTime_ColumnServerTime'),
'segment' => 'visitServerHour',
'sqlSegment' => 'HOUR(log_visit.visit_last_action_time)',
@@ -122,7 +122,7 @@ class Piwik_VisitTime extends Piwik_Plugin
);
$segments[] = array(
'type' => 'dimension',
- 'category' => 'Visit',
+ 'category' => Piwik_Translate('General_Visit'),
'name' => Piwik_Translate('VisitTime_ColumnLocalTime'),
'segment' => 'visitLocalHour',
'sqlSegment' => 'HOUR(log_visit.visitor_localtime)',
@@ -130,96 +130,25 @@ class Piwik_VisitTime extends Piwik_Plugin
);
}
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
function archivePeriod($notification)
{
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $dataTableToSum = array(
- 'VisitTime_localTime',
- 'VisitTime_serverTime',
- );
- $archiveProcessing->archiveDataTable($dataTableToSum);
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
- public function archiveDay($notification)
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveDayAggregateVisits($archiveProcessing);
- $this->archiveDayAggregateGoals($archiveProcessing);
- $this->archiveDayRecordInDatabase($archiveProcessing);
- }
-
- protected function archiveDayAggregateVisits($archiveProcessing)
- {
- $labelSQL = "HOUR(log_visit.visitor_localtime)";
- $this->interestByLocalTime = $archiveProcessing->getArrayInterestForLabel($labelSQL);
-
- $labelSQL = "HOUR(log_visit.visit_last_action_time)";
- $this->interestByServerTime = $archiveProcessing->getArrayInterestForLabel($labelSQL);
- }
-
- protected function convertServerTimeToLocalTimezone($interestByServerTime, $archiveProcessing)
- {
- $date = Piwik_Date::factory($archiveProcessing->getStartDatetimeUTC())->toString();
- $timezone = $archiveProcessing->site->getTimezone();
- $visitsByHourTz = array();
- foreach ($interestByServerTime as $hour => $stats) {
- $datetime = $date . ' ' . $hour . ':00:00';
- $hourInTz = (int)Piwik_Date::factory($datetime, $timezone)->toString('H');
- $visitsByHourTz[$hourInTz] = $stats;
- }
- return $visitsByHourTz;
- }
-
- protected function archiveDayAggregateGoals($archiveProcessing)
- {
- $query = $archiveProcessing->queryConversionsByDimension("HOUR(log_conversion.server_time)");
+ $archiveProcessor = $notification->getNotificationObject();
- if ($query === false) return;
-
- while ($row = $query->fetch()) {
- if (!isset($this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow($row['idgoal']);
- $archiveProcessing->updateGoalStats($row, $this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]);
+ $archiving = new Piwik_VisitTime_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
}
- $goalByServerTime = $this->convertServerTimeToLocalTimezone($this->interestByServerTime, $archiveProcessing);
- $archiveProcessing->enrichConversionsByLabelArray($this->interestByServerTime);
}
- protected function archiveDayRecordInDatabase($archiveProcessing)
- {
- $tableLocalTime = $archiveProcessing->getDataTableFromArray($this->interestByLocalTime);
- $this->makeSureAllHoursAreSet($tableLocalTime, $archiveProcessing);
- $archiveProcessing->insertBlobRecord('VisitTime_localTime', $tableLocalTime->getSerialized());
- destroy($tableLocalTime);
-
- $this->interestByServerTime = $this->convertServerTimeToLocalTimezone($this->interestByServerTime, $archiveProcessing);
- $tableServerTime = $archiveProcessing->getDataTableFromArray($this->interestByServerTime);
- $this->makeSureAllHoursAreSet($tableServerTime, $archiveProcessing);
- $archiveProcessing->insertBlobRecord('VisitTime_serverTime', $tableServerTime->getSerialized());
- destroy($tableServerTime);
- }
- private function makeSureAllHoursAreSet($table, $archiveProcessing)
+ public function archiveDay($notification)
{
- for ($i = 0; $i <= 23; $i++) {
- if ($table->getRowFromLabel($i) === false) {
- $row = $archiveProcessing->getNewInterestRowLabeled($i);
- $table->addRow($row);
- }
+ $archiveProcessor = $notification->getNotificationObject();
+ $archiving = new Piwik_VisitTime_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archiveDay();
}
}
+
}
diff --git a/plugins/VisitorGenerator/Controller.php b/plugins/VisitorGenerator/Controller.php
index 62825bdbd1..e93e99dd25 100644
--- a/plugins/VisitorGenerator/Controller.php
+++ b/plugins/VisitorGenerator/Controller.php
@@ -76,7 +76,7 @@ class Piwik_VisitorGenerator_Controller extends Piwik_Controller_Admin
$api = Piwik_CoreAdminHome_API::getInstance();
$api->invalidateArchivedReports($idSite, implode($dates, ","));
- $browserArchiving = Piwik_ArchiveProcessing::isBrowserTriggerArchivingEnabled();
+ $browserArchiving = Piwik_ArchiveProcessor_Rules::isBrowserTriggerEnabled();
// Init view
$view = new Piwik_View('@VisitorGenerator/generate');
diff --git a/plugins/VisitorInterest/API.php b/plugins/VisitorInterest/API.php
index 7adfd7ca4e..deaec5a68e 100644
--- a/plugins/VisitorInterest/API.php
+++ b/plugins/VisitorInterest/API.php
@@ -27,7 +27,7 @@ class Piwik_VisitorInterest_API
return self::$instance;
}
- protected function getDataTable($name, $idSite, $period, $date, $segment, $column = Piwik_Archive::INDEX_NB_VISITS)
+ protected function getDataTable($name, $idSite, $period, $date, $segment, $column = Piwik_Metrics::INDEX_NB_VISITS)
{
Piwik::checkUserHasViewAccess($idSite);
$archive = Piwik_Archive::build($idSite, $period, $date, $segment);
@@ -38,7 +38,7 @@ class Piwik_VisitorInterest_API
public function getNumberOfVisitsPerVisitDuration($idSite, $period, $date, $segment = false)
{
- $dataTable = $this->getDataTable('VisitorInterest_timeGap', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_VisitorInterest_Archiver::TIME_SPENT_RECORD_NAME, $idSite, $period, $date, $segment);
$dataTable->queueFilter('Sort', array('label', 'asc', true));
$dataTable->queueFilter('BeautifyTimeRangeLabels', array(
Piwik_Translate('VisitorInterest_BetweenXYSeconds'),
@@ -49,7 +49,7 @@ class Piwik_VisitorInterest_API
public function getNumberOfVisitsPerPage($idSite, $period, $date, $segment = false)
{
- $dataTable = $this->getDataTable('VisitorInterest_pageGap', $idSite, $period, $date, $segment);
+ $dataTable = $this->getDataTable(Piwik_VisitorInterest_Archiver::PAGES_VIEWED_RECORD_NAME, $idSite, $period, $date, $segment);
$dataTable->queueFilter('Sort', array('label', 'asc', true));
$dataTable->queueFilter('BeautifyRangeLabels', array(
Piwik_Translate('VisitorInterest_OnePage'),
@@ -69,9 +69,8 @@ class Piwik_VisitorInterest_API
*/
public function getNumberOfVisitsByDaysSinceLast($idSite, $period, $date, $segment = false)
{
- $recordName = 'VisitorInterest_daysSinceLastVisit';
$dataTable = $this->getDataTable(
- $recordName, $idSite, $period, $date, $segment, Piwik_Archive::INDEX_NB_VISITS);
+ Piwik_VisitorInterest_Archiver::DAYS_SINCE_LAST_RECORD_NAME, $idSite, $period, $date, $segment, Piwik_Metrics::INDEX_NB_VISITS);
$dataTable->queueFilter('BeautifyRangeLabels', array(
Piwik_Translate('General_OneDay'), Piwik_Translate('General_NDays')));
@@ -92,7 +91,7 @@ class Piwik_VisitorInterest_API
public function getNumberOfVisitsByVisitCount($idSite, $period, $date, $segment = false)
{
$dataTable = $this->getDataTable(
- 'VisitorInterest_visitsByVisitCount', $idSite, $period, $date, $segment, Piwik_Archive::INDEX_NB_VISITS);
+ Piwik_VisitorInterest_Archiver::VISITS_COUNT_RECORD_NAME, $idSite, $period, $date, $segment, Piwik_Metrics::INDEX_NB_VISITS);
$dataTable->queueFilter('BeautifyRangeLabels', array(
Piwik_Translate('General_OneVisit'), Piwik_Translate('General_NVisits')));
@@ -117,7 +116,7 @@ class Piwik_VisitorInterest_API
self::addVisitsPercentColumn($table);
}
} else {
- $totalVisits = array_sum($dataTable->getColumn(Piwik_Archive::INDEX_NB_VISITS));
+ $totalVisits = array_sum($dataTable->getColumn(Piwik_Metrics::INDEX_NB_VISITS));
$dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array(
'nb_visits_percentage', 'nb_visits', $totalVisits));
}
diff --git a/plugins/VisitorInterest/Archiver.php b/plugins/VisitorInterest/Archiver.php
new file mode 100644
index 0000000000..1720589a7f
--- /dev/null
+++ b/plugins/VisitorInterest/Archiver.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_VisitorInterest
+ */
+
+class Piwik_VisitorInterest_Archiver extends Piwik_PluginsArchiver
+{
+ // third element is unit (s for seconds, default is munutes)
+ const TIME_SPENT_RECORD_NAME = 'VisitorInterest_timeGap';
+ const PAGES_VIEWED_RECORD_NAME = 'VisitorInterest_pageGap';
+ const VISITS_COUNT_RECORD_NAME = 'VisitorInterest_visitsByVisitCount';
+ const DAYS_SINCE_LAST_RECORD_NAME = 'VisitorInterest_daysSinceLastVisit';
+
+ protected static $timeGap = array(
+ array(0, 10, 's'),
+ array(11, 30, 's'),
+ array(31, 60, 's'),
+ array(1, 2),
+ array(2, 4),
+ array(4, 7),
+ array(7, 10),
+ array(10, 15),
+ array(15, 30),
+ array(30)
+ );
+ protected static $pageGap = array(
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 7),
+ array(8, 10),
+ array(11, 14),
+ array(15, 20),
+ array(20)
+ );
+ /**
+ * The set of ranges used when calculating the 'visitors who visited at least N times' report.
+ */
+ protected static $visitNumberGap = array(
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 6),
+ array(7, 7),
+ array(8, 8),
+ array(9, 14),
+ array(15, 25),
+ array(26, 50),
+ array(51, 100),
+ array(101, 200),
+ array(200)
+ );
+ /**
+ * The set of ranges used when calculating the 'days since last visit' report.
+ */
+ protected static $daysSinceLastVisitGap = array(
+ array(0, 0),
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 6),
+ array(7, 7),
+ array(8, 14),
+ array(15, 30),
+ array(31, 60),
+ array(61, 120),
+ array(121, 364),
+ array(364)
+ );
+
+ public function archiveDay()
+ {
+ // these prefixes are prepended to the 'SELECT as' parts of each SELECT expression. detecting
+ // these prefixes allows us to get all the data in one query.
+ $prefixes = array(
+ self::TIME_SPENT_RECORD_NAME => 'tg',
+ self::PAGES_VIEWED_RECORD_NAME => 'pg',
+ self::VISITS_COUNT_RECORD_NAME => 'vbvn',
+ self::DAYS_SINCE_LAST_RECORD_NAME => 'dslv',
+ );
+
+ $aggregatesMetadata = array(
+ array('visit_total_time', self::getSecondsGap(), 'log_visit', $prefixes[self::TIME_SPENT_RECORD_NAME]),
+ array('visit_total_actions', self::$pageGap, 'log_visit', $prefixes[self::PAGES_VIEWED_RECORD_NAME]),
+ array('visitor_count_visits', self::$visitNumberGap, 'log_visit', $prefixes[self::VISITS_COUNT_RECORD_NAME]),
+ array('visitor_days_since_last', self::$daysSinceLastVisitGap, 'log_visit', $prefixes[self::DAYS_SINCE_LAST_RECORD_NAME],
+ $i_am_your_nightmare_DELETE_ME = true
+ ),
+ );
+ $selects = array();
+ foreach($aggregatesMetadata as $aggregateMetadata) {
+ $selectsFromRangedColumn = Piwik_DataAccess_LogAggregator::getSelectsFromRangedColumn($aggregateMetadata);
+ $selects = array_merge( $selects, $selectsFromRangedColumn);
+ }
+ $query = $this->getLogAggregator()->queryVisitsByDimension(array(), $where = false, $selects, array());
+ $row = $query->fetch();
+ foreach($prefixes as $recordName => $selectAsPrefix) {
+ $cleanRow = Piwik_DataAccess_LogAggregator::makeArrayOneColumn($row, Piwik_Metrics::INDEX_NB_VISITS, $selectAsPrefix);
+ $dataTable = Piwik_DataTable::makeFromIndexedArray($cleanRow);
+ $this->getProcessor()->insertBlobRecord($recordName, $dataTable->getSerialized());
+ }
+ }
+
+ /**
+ * Transforms and returns the set of ranges used to calculate the 'visits by total time'
+ * report from ranges in minutes to equivalent ranges in seconds.
+ */
+ protected static function getSecondsGap()
+ {
+ $secondsGap = array();
+ foreach (self::$timeGap as $gap) {
+ if (count($gap) == 3 && $gap[2] == 's') // if the units are already in seconds, just assign them
+ {
+ $secondsGap[] = array($gap[0], $gap[1]);
+ } else if (count($gap) == 2) {
+ $secondsGap[] = array($gap[0] * 60, $gap[1] * 60);
+ } else {
+ $secondsGap[] = array($gap[0] * 60);
+ }
+ }
+ return $secondsGap;
+ }
+
+ public function archivePeriod()
+ {
+ $dataTableToSum = array(
+ self::TIME_SPENT_RECORD_NAME,
+ self::PAGES_VIEWED_RECORD_NAME,
+ self::VISITS_COUNT_RECORD_NAME,
+ self::DAYS_SINCE_LAST_RECORD_NAME
+ );
+ $this->getProcessor()->aggregateDataTableReports($dataTableToSum);
+ }
+} \ No newline at end of file
diff --git a/plugins/VisitorInterest/VisitorInterest.php b/plugins/VisitorInterest/VisitorInterest.php
index 00689e5c2c..c2a46f1f9e 100644
--- a/plugins/VisitorInterest/VisitorInterest.php
+++ b/plugins/VisitorInterest/VisitorInterest.php
@@ -123,192 +123,26 @@ class Piwik_VisitorInterest extends Piwik_Plugin
Piwik_AddAction('template_footerVisitsFrequency', array('Piwik_VisitorInterest', 'footerVisitsFrequency'));
}
- // third element is unit (s for seconds, default is munutes)
- protected static $timeGap = array(
- array(0, 10, 's'),
- array(11, 30, 's'),
- array(31, 60, 's'),
- array(1, 2),
- array(2, 4),
- array(4, 7),
- array(7, 10),
- array(10, 15),
- array(15, 30),
- array(30)
- );
-
- protected static $pageGap = array(
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 7),
- array(8, 10),
- array(11, 14),
- array(15, 20),
- array(20)
- );
-
- /**
- * The set of ranges used when calculating the 'visitors who visited at least N times' report.
- */
- protected static $visitNumberGap = array(
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 6),
- array(7, 7),
- array(8, 8),
- array(9, 14),
- array(15, 25),
- array(26, 50),
- array(51, 100),
- array(101, 200),
- array(200)
- );
-
- /**
- * The set of ranges used when calculating the 'days since last visit' report.
- */
- protected static $daysSinceLastVisitGap = array(
- array(0, 0),
- array(1, 1),
- array(2, 2),
- array(3, 3),
- array(4, 4),
- array(5, 5),
- array(6, 6),
- array(7, 7),
- array(8, 14),
- array(15, 30),
- array(31, 60),
- array(61, 120),
- array(121, 364),
- array(364)
- );
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
function archivePeriod($notification)
{
- $archiveProcessing = $notification->getNotificationObject();
-
- if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+ $archiveProcessor = $notification->getNotificationObject();
- $dataTableToSum = array(
- 'VisitorInterest_timeGap',
- 'VisitorInterest_pageGap',
- 'VisitorInterest_visitsByVisitCount',
- 'VisitorInterest_daysSinceLastVisit'
- );
- $archiveProcessing->archiveDataTable($dataTableToSum);
+ $archiving = new Piwik_VisitorInterest_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archivePeriod();
+ }
}
- /**
- * @param Piwik_Event_Notification $notification notification object
- * @return mixed
- */
public function archiveDay($notification)
{
- $this->archiveProcessing = $notification->getNotificationObject();
-
- if (!$this->archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- // these prefixes are prepended to the 'SELECT as' parts of each SELECT expression. detecting
- // these prefixes allows us to get all the data in one query.
- $timeGapPrefix = 'tg';
- $pageGapPrefix = 'pg';
- $visitsByVisitNumPrefix = 'vbvn';
- $daysSinceLastVisitPrefix = 'dslv';
+ $archiveProcessor = $notification->getNotificationObject();
- // extra condition for the SQL SELECT that makes sure only returning visits are counted
- // when creating the 'days since last visit' report. the SELECT expression below it
- // is used to count all new visits.
- $daysSinceLastExtraCondition = 'and log_visit.visitor_returning = 1';
- $selectAs = $daysSinceLastVisitPrefix . 'General_NewVisits';
- $newVisitCountSelect = "sum(case when log_visit.visitor_returning = 0 then 1 else 0 end) as `$selectAs`";
-
- // create the select expressions to use
- $timeGapSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visit_total_time', self::getSecondsGap(), 'log_visit', $timeGapPrefix);
- $pageGapSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visit_total_actions', self::$pageGap, 'log_visit', $pageGapPrefix);
- $visitsByVisitNumSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_count_visits', self::$visitNumberGap, 'log_visit', $visitsByVisitNumPrefix);
-
- $daysSinceLastVisitSelects = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_days_since_last', self::$daysSinceLastVisitGap, 'log_visit', $daysSinceLastVisitPrefix,
- $daysSinceLastExtraCondition);
- array_unshift($daysSinceLastVisitSelects, $newVisitCountSelect);
-
- $selects = array_merge(
- $timeGapSelects, $pageGapSelects, $visitsByVisitNumSelects, $daysSinceLastVisitSelects);
-
- // select data for every report
- $row = $this->archiveProcessing->queryVisitsSimple(implode(',', $selects));
-
- // archive visits by total time report
- $recordName = 'VisitorInterest_timeGap';
- $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $timeGapPrefix);
-
- // archive visits by total actions report
- $recordName = 'VisitorInterest_pageGap';
- $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $pageGapPrefix);
-
- // archive visits by visit number report
- $recordName = 'VisitorInterest_visitsByVisitCount';
- $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $visitsByVisitNumPrefix);
-
- // archive days since last visit report
- $recordName = 'VisitorInterest_daysSinceLastVisit';
- $this->archiveRangeStats($recordName, $row, Piwik_Archive::INDEX_NB_VISITS, $daysSinceLastVisitPrefix);
- }
-
- /**
- * Transforms and returns the set of ranges used to calculate the 'visits by total time'
- * report from ranges in minutes to equivalent ranges in seconds.
- */
- protected static function getSecondsGap()
- {
- $secondsGap = array();
- foreach (self::$timeGap as $gap) {
- if (count($gap) == 3 && $gap[2] == 's') // if the units are already in seconds, just assign them
- {
- $secondsGap[] = array($gap[0], $gap[1]);
- } else if (count($gap) == 2) {
- $secondsGap[] = array($gap[0] * 60, $gap[1] * 60);
- } else {
- $secondsGap[] = array($gap[0] * 60);
- }
+ $archiving = new Piwik_VisitorInterest_Archiver($archiveProcessor);
+ if($archiving->shouldArchive()) {
+ $archiving->archiveDay();
}
- return $secondsGap;
}
- /**
- * Creates and archives a DataTable from some (or all) elements of a supplied database
- * row.
- *
- * @param string $recordName The record name to use when inserting the new archive.
- * @param array $row The database row to use.
- * @param string $selectAsPrefix The string to look for as the prefix of SELECT as
- * expressions. Elements in $row that have a SELECT as
- * with this string as a prefix are used in creating
- * the DataTable.'
- */
- protected function archiveRangeStats($recordName, $row, $index, $selectAsPrefix)
- {
- // create the DataTable from parts of the result row
- $dataTable = $this->archiveProcessing->getSimpleDataTableFromRow($row, $index, $selectAsPrefix);
-
- // insert the data table as a blob archive
- $this->archiveProcessing->insertBlobRecord($recordName, $dataTable->getSerialized());
- destroy($dataTable);
- }
/**
* @param Piwik_Event_Notification $notification notification object
diff --git a/plugins/VisitsSummary/API.php b/plugins/VisitsSummary/API.php
index 0864c517b5..3205edaf61 100644
--- a/plugins/VisitsSummary/API.php
+++ b/plugins/VisitsSummary/API.php
@@ -62,19 +62,7 @@ class Piwik_VisitsSummary_API
$columns = array_merge($columns, $tempColumns);
} else {
$bounceRateRequested = $actionsPerVisitRequested = $averageVisitDurationRequested = true;
- $columns = array(
- 'nb_visits',
- 'nb_actions',
- 'nb_visits_converted',
- 'bounce_count',
- 'sum_visit_length',
- 'max_actions'
- );
- if (Piwik::isUniqueVisitorsEnabled($period)) {
- $columns = array_merge(array('nb_uniq_visitors'), $columns);
- }
- // Force reindex from 0 to N otherwise the SQL bind will fail
- $columns = array_values($columns);
+ $columns = $this->getCoreColumns($period);
}
$dataTable = $archive->getDataTableFromNumeric($columns);
@@ -96,11 +84,38 @@ class Piwik_VisitsSummary_API
return $dataTable;
}
+ /**
+ * @ignore
+ */
+ public function getColumns($period)
+ {
+ $columns = $this->getCoreColumns($period);
+ $columns = array_merge($columns, array('bounce_rate', 'nb_actions_per_visit', 'avg_time_on_site'));
+ return $columns;
+ }
+
+ protected function getCoreColumns($period)
+ {
+ $columns = array(
+ 'nb_visits',
+ 'nb_actions',
+ 'nb_visits_converted',
+ 'bounce_count',
+ 'sum_visit_length',
+ 'max_actions'
+ );
+ if (Piwik::isUniqueVisitorsEnabled($period)) {
+ $columns = array_merge(array('nb_uniq_visitors'), $columns);
+ }
+ $columns = array_values($columns);
+ return $columns;
+ }
+
protected function getNumeric($idSite, $period, $date, $segment, $toFetch)
{
Piwik::checkUserHasViewAccess($idSite);
$archive = Piwik_Archive::build($idSite, $period, $date, $segment);
- $dataTable = $archive->getNumeric($toFetch);
+ $dataTable = $archive->getDataTableFromNumeric($toFetch);
return $dataTable;
}
@@ -142,8 +157,9 @@ class Piwik_VisitsSummary_API
public function getSumVisitsLengthPretty($idSite, $period, $date, $segment = false)
{
$table = $this->getSumVisitsLength($idSite, $period, $date, $segment);
- if ($table instanceof Piwik_DataTable_Array) {
- $table->filter('ColumnCallbackReplace', array(0, array('Piwik', 'getPrettyTimeFromSeconds')));
+ if (is_object($table)) {
+ $table->filter('ColumnCallbackReplace',
+ array('sum_visit_length', array('Piwik', 'getPrettyTimeFromSeconds')));
} else {
$table = Piwik::getPrettyTimeFromSeconds($table);
}
diff --git a/plugins/VisitsSummary/Controller.php b/plugins/VisitsSummary/Controller.php
index dcd1313da3..8eaa3f21e3 100644
--- a/plugins/VisitsSummary/Controller.php
+++ b/plugins/VisitsSummary/Controller.php
@@ -129,7 +129,7 @@ class Piwik_VisitsSummary_Controller extends Piwik_Controller
$dataTableVisit = self::getVisitsSummary();
$dataRow = $dataTableVisit->getRowsCount() == 0 ? new Piwik_DataTable_Row() : $dataTableVisit->getFirstRow();
- $dataTableActions = Piwik_Actions_API::getInstance()->get($idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), Piwik_Common::getRequestVar('segment', false));
+ $dataTableActions = Piwik_Actions_API::getInstance()->get($idSite, Piwik_Common::getRequestVar('period'), Piwik_Common::getRequestVar('date'), Piwik_ViewDataTable::getRawSegmentFromRequest());
$dataActionsRow =
$dataTableActions->getRowsCount() == 0 ? new Piwik_DataTable_Row() : $dataTableActions->getFirstRow();
diff --git a/plugins/VisitsSummary/VisitsSummary.php b/plugins/VisitsSummary/VisitsSummary.php
index 87b320c947..0744b0424a 100644
--- a/plugins/VisitsSummary/VisitsSummary.php
+++ b/plugins/VisitsSummary/VisitsSummary.php
@@ -12,7 +12,7 @@
/**
* Note: This plugin does not hook on Daily and Period Archiving like other Plugins because it reports the
* very core metrics (visits, actions, visit duration, etc.) which are processed in the Core
- * Piwik_ArchiveProcessing_Day class directly.
+ * Piwik_ArchiveProcessor_Day class directly.
* These metrics can be used by other Plugins so they need to be processed up front.
*
* @package Piwik_VisitsSummary
diff --git a/plugins/Widgetize/Widgetize.php b/plugins/Widgetize/Widgetize.php
index 2ae9d74e11..24c50c280f 100644
--- a/plugins/Widgetize/Widgetize.php
+++ b/plugins/Widgetize/Widgetize.php
@@ -39,7 +39,7 @@ class Piwik_Widgetize extends Piwik_Plugin
public function addTopMenu()
{
$tooltip = Piwik_Translate('Widgetize_TopLinkTooltip');
- $urlParams = array('module' => 'Widgetize', 'action' => 'index');
+ $urlParams = array('module' => 'Widgetize', 'action' => 'index', 'segment' => false);
Piwik_AddTopMenu('General_Widgets', $urlParams, true, 5, $isHTML = false, $tooltip);
}
diff --git a/plugins/Zeitgeist/javascripts/ajaxHelper.js b/plugins/Zeitgeist/javascripts/ajaxHelper.js
index accc3501c0..7f6349429a 100644
--- a/plugins/Zeitgeist/javascripts/ajaxHelper.js
+++ b/plugins/Zeitgeist/javascripts/ajaxHelper.js
@@ -56,7 +56,7 @@ function ajaxHelper() {
this.format = 'json';
/**
- * Should ajax request be synchronous
+ * Should ajax request be asynchronous
* @type {Boolean}
*/
this.async = true;
@@ -66,11 +66,11 @@ function ajaxHelper() {
*/
this.callback = function () {};
- /**
- * Use this.callback if an error is returned
- * @type {Boolean}
- */
- this.useRegularCallbackInCaseOfError = false;
+ /**
+ * Use this.callback if an error is returned
+ * @type {Boolean}
+ */
+ this.useRegularCallbackInCaseOfError = false;
/**
* Callback function to be executed on error
@@ -118,18 +118,12 @@ function ajaxHelper() {
* @return {void}
*/
this.addParams = function (params, type) {
- switch (type.toLowerCase()) {
-
- case 'get':
- for (var key in params) {
- this.getParams[key] = params[key];
- }
- break;
- case 'post':
- for (var key in params) {
- this.postParams[key] = params[key];
- }
- break;
+ for (var key in params) {
+ if(type.toLowerCase() == 'get') {
+ this.getParams[key] = params[key];
+ } else if(type.toLowerCase() == 'post') {
+ this.postParams[key] = params[key];
+ }
}
};
@@ -138,18 +132,17 @@ function ajaxHelper() {
* function is a single request to use.
*/
this.setBulkRequests = function () {
- var urls = [];
- for (var i = 0; i != arguments.length; ++i)
- {
- urls.push($.param(arguments[i]));
- }
-
- this.addParams({
- module: 'API',
- method: 'API.getBulkRequest',
- urls: urls,
- format: 'json'
- }, 'post');
+ var urls = [];
+ for (var i = 0; i != arguments.length; ++i) {
+ urls.push($.param(arguments[i]));
+ }
+
+ this.addParams({
+ module: 'API',
+ method: 'API.getBulkRequest',
+ urls: urls,
+ format: 'json'
+ }, 'post');
};
/**
@@ -162,15 +155,13 @@ function ajaxHelper() {
this.callback = callback;
};
- /**
- * Set that the callback passed to setCallback() should be used if an application error (i.e. an
- * Exception in PHP) is returned.
- *
- * @param {void}
- */
- this.useCallbackInCaseOfError = function () {
- this.useRegularCallbackInCaseOfError = true;
- };
+ /**
+ * Set that the callback passed to setCallback() should be used if an application error (i.e. an
+ * Exception in PHP) is returned.
+ */
+ this.useCallbackInCaseOfError = function () {
+ this.useRegularCallbackInCaseOfError = true;
+ };
/**
* Set callback to redirect on success handler
@@ -306,10 +297,24 @@ function ajaxHelper() {
this._buildAjaxCall = function () {
var that = this;
+ var parameters = this._mixinDefaultGetParams(this.getParams);
+
+ var url = 'index.php?';
+
+ // we took care of encoding &segment properly already, so we don't use $.param for it ($.param URL encodes the values)
+ if(parameters['segment']) {
+ url += 'segment=' + parameters['segment'] + '&';
+ delete parameters['segment'];
+ }
+ if(parameters['date']) {
+ url += 'date=' + decodeURIComponent(parameters['date']) + '&';
+ delete parameters['date'];
+ }
+ url += $.param(parameters);
var ajaxCall = {
type: 'POST',
async: this.async !== false,
- url: 'index.php?' + $.param(this._mixinDefaultGetParams(this.getParams)),
+ url: url,
dataType: this.format || 'json',
error: this.errorCallback,
success: function (response) {
@@ -369,7 +374,7 @@ function ajaxHelper() {
var defaultParams = {
idSite: piwik.idSite || broadcast.getValueFromUrl('idSite'),
period: piwik.period || broadcast.getValueFromUrl('period'),
- segment: broadcast.getValueFromHash('segment', window.location.href)
+ segment: broadcast.getValueFromHash('segment', window.location.href.split('#')[1])
};
// never append token_auth to url
@@ -379,13 +384,13 @@ function ajaxHelper() {
}
for (var key in defaultParams) {
- if (!params[key] && defaultParams[key]) {
+ if (!params[key] && !this.postParams[key] && defaultParams[key]) {
params[key] = defaultParams[key];
}
}
// handle default date & period if not already set
- if (!params.date) {
+ if (!params.date && !this.postParams.date) {
params.date = piwik.currentDateString || broadcast.getValueFromUrl('date');
if (params.period == 'range' && piwik.currentDateString) {
params.date = piwik.startDateString + ',' + params.date;
diff --git a/plugins/Zeitgeist/javascripts/piwikHelper.js b/plugins/Zeitgeist/javascripts/piwikHelper.js
index 2e84968968..a2ade4c846 100644
--- a/plugins/Zeitgeist/javascripts/piwikHelper.js
+++ b/plugins/Zeitgeist/javascripts/piwikHelper.js
@@ -113,6 +113,32 @@ var piwikHelper = {
});
},
+ getQueryStringWithParametersModified: function (queryString, newParameters) {
+ if (queryString != '') {
+ var r, i, keyvalue, keysvalues = newParameters.split('&');
+ var appendUrl = '';
+ for (i = 0; i < keysvalues.length; i++) {
+ keyvalue = keysvalues[i].split('=');
+ r = new RegExp('(^|[?&])' + keyvalue[0] + '=[^&]*');
+ queryString = queryString.replace(r, '');
+
+ // empty value, eg. &segment=, we remove the parameter from URL entirely
+ if (keyvalue[1].length == 0) {
+ continue;
+ }
+ appendUrl += '&' + keyvalue[0] + '=' + keyvalue[1];
+ }
+ queryString += appendUrl;
+ if (queryString[0] == '&') {
+ queryString = '?' + queryString.substring(1);
+ }
+ } else {
+ queryString = '?' + newParameters;
+ }
+
+ return queryString;
+ },
+
/**
* Returns the current query string with the given parameters modified
* @param {object} newparams parameters to be modified
@@ -120,24 +146,13 @@ var piwikHelper = {
*/
getCurrentQueryStringWithParametersModified: function(newparams)
{
- var parameters = String(window.location.search);
- if(newparams) {
- if(parameters != '') {
- var r, i, keyvalue, keysvalues = newparams.split('&');
- for(i = 0; i < keysvalues.length; i++) {
- keyvalue = keysvalues[i].split('=');
- r = new RegExp('(^|[?&])'+keyvalue[0]+'=[^&]*');
- parameters = parameters.replace(r, '');
- }
- parameters += '&' + newparams;
- if(parameters[0] == '&') {
- parameters = '?' + parameters.substring(1);
- }
- } else {
- parameters = '?' + newparams;
- }
+ var queryString = String(window.location.search);
+ if (newparams) {
+ queryString = this.getQueryStringWithParametersModified(queryString, newparams);
}
- return String(window.location.pathname) + parameters;
+ var value = String(window.location.pathname) + queryString;
+
+ return value;
},
/**
@@ -365,6 +380,9 @@ var piwikHelper = {
*/
getApiFormatTextarea: function (textareaContent)
{
+ if(typeof textareaContent == 'undefined') {
+ return '';
+ }
return textareaContent.trim().split("\n").join(',');
}
diff --git a/plugins/Zeitgeist/stylesheets/common.css b/plugins/Zeitgeist/stylesheets/common.css
index 2d8db49e8b..feb94ad7d6 100644
--- a/plugins/Zeitgeist/stylesheets/common.css
+++ b/plugins/Zeitgeist/stylesheets/common.css
@@ -78,6 +78,15 @@ a {
.loadingPiwik img {
margin-right:5px;
}
+
+.loadingSegment {
+ color: grey;
+ display: block;
+ font-size: 10pt;
+ margin-left: 28px;
+ display:none;
+}
+
#loadingError {
font-weight: bold;
font-size: 1.1em;
@@ -105,7 +114,7 @@ a {
}
#periodString {
- display:block;
+ display:block;
color:#444;
font-size:14px;
border: 1px solid #e4e5e4;
@@ -118,14 +127,24 @@ a {
z-index:999;
position:absolute;
background: url("../images/icon-calendar.gif") no-repeat scroll right 9px center #F7F7F7;
+ background-color: #f7f7f7;
}
#periodString:hover {
background-color:#f1f0eb;
border-color:#a9a399;
}
+#periodString .calendar-icon {
+ width: 13px;
+ height: 15px;
+ display:inline-block;
+ position:absolute;
+ right:9px;
+ top:7px;
+ background: url("images/icon-calendar.gif") no-repeat scroll;
+ cursor:pointer;
+}
#periodString #date{
- cursor:pointer;
- display:inline-block;
+ cursor:pointer;
padding:5px 10px 6px 10px;
margin:-5px -10px -6px -10px;