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:
-rw-r--r--.travis.yml8
-rw-r--r--CHANGELOG.md7
-rw-r--r--composer.json3
-rw-r--r--composer.lock116
-rw-r--r--core/API/DataTableManipulator.php3
-rw-r--r--core/API/DataTableManipulator/Flattener.php1
-rw-r--r--core/API/DataTableManipulator/ReportTotalsCalculator.php1
-rw-r--r--core/API/DocumentationGenerator.php101
-rw-r--r--core/API/Proxy.php2
-rw-r--r--core/API/Request.php2
-rw-r--r--core/API/ResponseBuilder.php5
-rw-r--r--core/Access.php110
-rw-r--r--core/Archive.php66
-rw-r--r--core/Archive/DataCollection.php3
-rw-r--r--core/Archive/DataTableFactory.php1
-rw-r--r--core/ArchiveProcessor.php32
-rw-r--r--core/ArchiveProcessor/Loader.php21
-rw-r--r--core/ArchiveProcessor/Parameters.php2
-rw-r--r--core/ArchiveProcessor/PluginsArchiver.php8
-rw-r--r--core/ArchiveProcessor/Rules.php19
-rw-r--r--core/AssetManager.php16
-rw-r--r--core/AssetManager/UIAssetCatalog.php2
-rw-r--r--core/AssetManager/UIAssetFetcher.php2
-rw-r--r--core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php8
-rw-r--r--core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php3
-rw-r--r--core/AssetManager/UIAssetMerger.php25
-rw-r--r--core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php2
-rw-r--r--core/AssetManager/UIAssetMinifier.php1
-rw-r--r--core/Auth.php34
-rw-r--r--core/BaseFactory.php1
-rw-r--r--core/CacheFile.php40
-rw-r--r--core/CliMulti.php27
-rw-r--r--core/CliMulti/CliPhp.php8
-rw-r--r--core/CliMulti/Process.php10
-rw-r--r--core/Columns/Updater.php33
-rw-r--r--core/Common.php29
-rw-r--r--core/Config.php9
-rw-r--r--core/Console.php29
-rw-r--r--core/Cookie.php27
-rw-r--r--core/CronArchive.php116
-rw-r--r--core/DataAccess/ArchivePurger.php69
-rw-r--r--core/DataAccess/ArchiveSelector.php110
-rw-r--r--core/DataAccess/ArchiveTableCreator.php33
-rw-r--r--core/DataAccess/ArchiveWriter.php117
-rw-r--r--core/DataAccess/LogAggregator.php78
-rw-r--r--core/DataAccess/Model.php229
-rw-r--r--core/DataArray.php8
-rw-r--r--core/DataTable.php10
-rw-r--r--core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php2
-rw-r--r--core/DataTable/Filter/BeautifyRangeLabels.php2
-rwxr-xr-xcore/DataTable/Filter/CalculateEvolutionFilter.php3
-rw-r--r--core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php1
-rw-r--r--core/DataTable/Filter/ColumnCallbackAddMetadata.php8
-rw-r--r--core/DataTable/Filter/ColumnCallbackDeleteRow.php1
-rw-r--r--core/DataTable/Filter/ColumnCallbackReplace.php7
-rw-r--r--core/DataTable/Filter/ColumnDelete.php1
-rwxr-xr-xcore/DataTable/Filter/GroupBy.php6
-rw-r--r--core/DataTable/Filter/Limit.php4
-rw-r--r--core/DataTable/Filter/Sort.php13
-rw-r--r--core/DataTable/Filter/Truncate.php5
-rw-r--r--core/DataTable/Manager.php2
-rw-r--r--core/DataTable/Map.php2
-rw-r--r--core/DataTable/Renderer.php12
-rw-r--r--core/DataTable/Renderer/Console.php2
-rw-r--r--core/DataTable/Renderer/Json.php1
-rw-r--r--core/DataTable/Row.php7
-rw-r--r--core/Date.php11
-rw-r--r--core/Db.php31
-rw-r--r--core/Db/Adapter.php2
-rw-r--r--core/Db/Adapter/Mysqli.php12
-rw-r--r--core/Db/Adapter/Pdo/Mssql.php9
-rw-r--r--core/Db/Adapter/Pdo/Mysql.php7
-rw-r--r--core/Db/Adapter/Pdo/Pgsql.php2
-rw-r--r--core/Db/BatchInsert.php21
-rw-r--r--core/Db/Schema.php9
-rw-r--r--core/Db/Schema/Mysql.php463
-rw-r--r--core/DeviceDetectorCache.php1
-rw-r--r--core/Error.php5
-rw-r--r--core/EventDispatcher.php2
-rw-r--r--core/ExceptionHandler.php1
-rw-r--r--core/Filechecks.php12
-rw-r--r--core/FrontController.php14
-rw-r--r--core/Http.php4
-rw-r--r--core/IP.php2
-rw-r--r--core/Log.php4
-rw-r--r--core/Mail.php31
-rw-r--r--core/Menu/Group.php4
-rw-r--r--core/Menu/MenuAbstract.php20
-rw-r--r--core/Menu/MenuAdmin.php14
-rw-r--r--core/Menu/MenuMain.php2
-rw-r--r--core/Menu/MenuReporting.php6
-rwxr-xr-xcore/Menu/MenuUser.php6
-rw-r--r--core/Metrics.php2
-rw-r--r--core/Metrics/Processed.php2
-rw-r--r--core/MetricsFormatter.php28
-rw-r--r--core/Nonce.php2
-rw-r--r--core/Option.php28
-rw-r--r--core/Period.php19
-rw-r--r--core/Period/Day.php9
-rw-r--r--core/Period/Factory.php2
-rw-r--r--core/Period/Month.php1
-rw-r--r--core/Period/Range.php24
-rw-r--r--core/Period/Week.php14
-rw-r--r--core/Period/Year.php3
-rw-r--r--core/Piwik.php8
-rw-r--r--core/Plugin.php2
-rw-r--r--core/Plugin/ComponentFactory.php2
-rw-r--r--core/Plugin/ConsoleCommand.php1
-rw-r--r--core/Plugin/Controller.php57
-rw-r--r--core/Plugin/ControllerAdmin.php10
-rw-r--r--core/Plugin/Manager.php16
-rw-r--r--core/Plugin/Menu.php79
-rw-r--r--core/Plugin/MetadataLoader.php1
-rw-r--r--core/Plugin/Report.php21
-rw-r--r--core/Plugin/ViewDataTable.php4
-rw-r--r--core/Plugin/Visualization.php1
-rw-r--r--core/Profiler.php6
-rw-r--r--core/ProxyHttp.php1
-rw-r--r--core/RankingQuery.php2
-rw-r--r--core/Registry.php3
-rw-r--r--core/ReportRenderer/Csv.php2
-rw-r--r--core/ScheduledTask.php1
-rw-r--r--core/ScheduledTime.php7
-rw-r--r--core/ScheduledTime/Monthly.php1
-rw-r--r--core/Segment.php8
-rw-r--r--core/SegmentExpression.php17
-rw-r--r--core/Session/SaveHandler/DbTable.php3
-rw-r--r--core/SettingsPiwik.php18
-rw-r--r--core/Site.php6
-rw-r--r--core/Theme.php12
-rw-r--r--core/Tracker.php12
-rw-r--r--core/Tracker/Action.php138
-rw-r--r--core/Tracker/ActionPageview.php38
-rw-r--r--core/Tracker/Cache.php73
-rw-r--r--core/Tracker/Db.php13
-rw-r--r--core/Tracker/Db/Mysqli.php120
-rw-r--r--core/Tracker/Db/Pdo/Mysql.php108
-rw-r--r--core/Tracker/GoalManager.php225
-rw-r--r--core/Tracker/Model.php383
-rw-r--r--core/Tracker/PageUrl.php51
-rw-r--r--core/Tracker/Request.php31
-rw-r--r--core/Tracker/TableLogAction.php89
-rw-r--r--core/Tracker/Visit.php198
-rw-r--r--core/Tracker/VisitExcluded.php18
-rw-r--r--core/Tracker/Visitor.php162
-rw-r--r--core/Translate.php4
-rw-r--r--core/Translate/Validate/NoScripts.php2
-rw-r--r--core/Translate/Writer.php4
-rwxr-xr-xcore/Twig.php4
-rw-r--r--core/Unzip.php14
-rwxr-xr-xcore/Unzip/Gzip.php81
-rw-r--r--core/Unzip/PclZip.php88
-rwxr-xr-xcore/Unzip/Tar.php84
-rw-r--r--core/Unzip/UncompressInterface.php39
-rw-r--r--core/Unzip/ZipArchive.php133
-rw-r--r--core/UpdateCheck.php2
-rw-r--r--core/Updater.php2
-rw-r--r--core/Updates/0.2.34.php28
-rw-r--r--core/Updates/0.6.2.php47
-rw-r--r--core/Updates/2.0-a17.php2
-rw-r--r--core/Updates/2.0-b13.php2
-rw-r--r--core/Updates/2.0.3-b7.php2
-rw-r--r--core/Updates/2.1.1-b11.php13
-rw-r--r--core/Updates/2.4.0-b1.php3
-rw-r--r--core/Url.php24
-rw-r--r--core/Version.php2
-rw-r--r--core/View.php4
-rw-r--r--core/View/ReportsByDimension.php6
-rw-r--r--core/ViewDataTable/Config.php2
-rw-r--r--core/ViewDataTable/Manager.php132
-rw-r--r--core/ViewDataTable/Request.php4
-rw-r--r--core/Visualization/Sparkline.php8
-rw-r--r--core/dispatch.php4
-rw-r--r--core/testMinimumPhpVersion.php2
-rwxr-xr-xlibs/Archive_Tar/Tar.php1993
-rw-r--r--libs/PiwikTracker/PiwikTracker.php3
-rw-r--r--libs/README.md2
-rw-r--r--misc/phpstorm-codestyles/Piwik_codestyle.xml133
-rw-r--r--misc/proxy-hide-piwik-url/piwik.php2
-rw-r--r--plugins/API/API.php6
-rw-r--r--plugins/API/Controller.php2
-rw-r--r--plugins/API/RowEvolution.php4
-rw-r--r--plugins/Actions/Actions/ActionSiteSearch.php6
-rw-r--r--plugins/Actions/Archiver.php2
-rwxr-xr-xplugins/Annotations/AnnotationList.php2
-rw-r--r--plugins/Contents/Controller.php2
-rw-r--r--plugins/Contents/tests/ContentsTest.php4
-rw-r--r--plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_day.xml6
-rw-r--r--plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_month.xml6
-rw-r--r--plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_day.xml714
-rw-r--r--plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_month.xml714
-rw-r--r--plugins/Contents/tests/expected/test_Contents_contentInteractionMatch__Live.getLastVisitsDetails_day.xml208
-rw-r--r--plugins/Contents/tests/expected/test_Contents_contentTargetMatch__Live.getLastVisitsDetails_day.xml376
-rw-r--r--plugins/Contents/tests/expected/test_ContentscontentNameOrPieceMatch__Live.getLastVisitsDetails_day.xml714
-rw-r--r--plugins/CoreAdminHome/API.php10
-rw-r--r--plugins/CoreAdminHome/Controller.php6
-rw-r--r--plugins/CoreAdminHome/CustomLogo.php4
-rw-r--r--plugins/CoreAdminHome/Tasks.php2
-rw-r--r--plugins/CoreConsole/Commands/GenerateCommand.php17
-rw-r--r--plugins/CoreConsole/Commands/GenerateTest.php4
-rw-r--r--plugins/CoreConsole/TravisYmlView.php5
-rw-r--r--plugins/CoreHome/Columns/UserId.php7
-rw-r--r--plugins/CoreHome/Columns/VisitGoalBuyer.php2
-rw-r--r--plugins/CoreHome/Controller.php14
-rw-r--r--plugins/CoreHome/CoreHome.php4
-rw-r--r--plugins/CoreHome/DataTableRowAction/RowEvolution.php4
-rw-r--r--plugins/CoreHome/Visitor.php4
-rw-r--r--plugins/CoreHome/angularjs/common/directives/translate.js6
-rw-r--r--plugins/CoreHome/angularjs/common/services/piwik-api.js10
-rw-r--r--plugins/CoreHome/angularjs/notification/notification.controller.js34
-rw-r--r--plugins/CoreHome/angularjs/notification/notification.directive.html8
-rw-r--r--plugins/CoreHome/angularjs/notification/notification.directive.js95
-rw-r--r--plugins/CoreHome/angularjs/notification/notification.directive.less84
-rw-r--r--plugins/CoreHome/javascripts/notification.js135
-rw-r--r--plugins/CoreHome/stylesheets/notification.less87
-rw-r--r--plugins/CoreHome/templates/_topBarTopMenu.twig11
-rw-r--r--plugins/CorePluginsAdmin/Controller.php4
-rw-r--r--plugins/CorePluginsAdmin/PluginInstaller.php2
-rw-r--r--plugins/CorePluginsAdmin/UpdateCommunication.php75
-rw-r--r--plugins/CoreUpdater/Commands/Update.php2
-rw-r--r--plugins/CoreUpdater/Controller.php4
-rw-r--r--plugins/CoreUpdater/UpdateCommunication.php6
-rw-r--r--plugins/CustomVariables/Archiver.php2
-rw-r--r--plugins/CustomVariables/tests/ModelTest.php22
-rw-r--r--plugins/CustomVariables/tests/expected/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml2
-rw-r--r--plugins/CustomVariables/tests/processed/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml2
-rw-r--r--plugins/Dashboard/Controller.php87
-rw-r--r--plugins/Dashboard/Dashboard.php27
-rw-r--r--plugins/Dashboard/Menu.php4
-rw-r--r--plugins/Dashboard/Model.php147
-rw-r--r--plugins/DevicesDetection/functions.php4
-rw-r--r--plugins/Events/API.php8
-rw-r--r--plugins/Events/Archiver.php2
-rw-r--r--plugins/Events/Events.php6
-rw-r--r--plugins/ExampleCommand/Commands/HelloWorld.php22
-rw-r--r--plugins/ExampleRssWidget/RssRenderer.php2
-rw-r--r--plugins/ExampleUI/templates/notifications.twig7
-rw-r--r--plugins/Goals/API.php93
-rw-r--r--plugins/Goals/Controller.php4
-rw-r--r--plugins/Goals/Goals.php3
-rw-r--r--plugins/Goals/Model.php95
-rw-r--r--plugins/Goals/Visualizations/Goals.php2
-rw-r--r--plugins/Goals/tests/APITest.php4
-rw-r--r--plugins/ImageGraph/StaticGraph/GridGraph.php8
-rw-r--r--plugins/Installation/Controller.php87
-rw-r--r--plugins/Installation/FormFirstWebsiteSetup.php8
-rw-r--r--plugins/Installation/ServerFilesGenerator.php2
-rw-r--r--plugins/Installation/SystemCheck.php2
-rw-r--r--plugins/LanguagesManager/API.php25
-rw-r--r--plugins/LanguagesManager/Commands/CompareKeys.php4
-rw-r--r--plugins/LanguagesManager/LanguagesManager.php11
-rw-r--r--plugins/LanguagesManager/Model.php72
-rw-r--r--plugins/Live/API.php12
-rw-r--r--plugins/Live/Visitor.php6
-rw-r--r--plugins/Live/VisitorLog.php2
-rw-r--r--plugins/Login/Controller.php5
-rw-r--r--plugins/Login/Login.php2
-rw-r--r--plugins/Login/PasswordResetter.php5
-rw-r--r--plugins/Login/SessionInitializer.php13
-rwxr-xr-xplugins/MultiSites/API.php2
-rw-r--r--plugins/MultiSites/Menu.php2
-rw-r--r--plugins/PrivacyManager/DoNotTrackHeaderChecker.php2
-rwxr-xr-xplugins/PrivacyManager/LogDataPurger.php2
-rwxr-xr-xplugins/PrivacyManager/ReportsPurger.php31
-rw-r--r--plugins/Referrers/Columns/Base.php8
-rw-r--r--plugins/Referrers/Controller.php4
-rw-r--r--plugins/Referrers/functions.php4
-rw-r--r--plugins/ScheduledReports/API.php92
-rw-r--r--plugins/ScheduledReports/Model.php93
-rw-r--r--plugins/ScheduledReports/ScheduledReports.php42
-rw-r--r--plugins/SegmentEditor/API.php43
-rw-r--r--plugins/SegmentEditor/Model.php74
-rw-r--r--plugins/SegmentEditor/SegmentEditor.php16
-rw-r--r--plugins/SitesManager/API.php218
-rw-r--r--plugins/SitesManager/Model.php293
-rw-r--r--plugins/Transitions/API.php7
-rw-r--r--plugins/UserCountry/Controller.php2
-rw-r--r--plugins/UsersManager/API.php13
-rw-r--r--plugins/UsersManager/Controller.php4
-rw-r--r--plugins/UsersManager/Menu.php8
-rw-r--r--plugins/UsersManager/Model.php97
-rw-r--r--plugins/UsersManager/UsersManager.php18
m---------plugins/VisitorGenerator0
-rw-r--r--plugins/VisitsSummary/Controller.php2
-rw-r--r--tests/PHPUnit/Core/AssetManagerTest.php2
-rw-r--r--tests/PHPUnit/Core/Http/fixture.zip (renamed from tests/PHPUnit/Core/Unzip/relative.zip)bin128 -> 128 bytes
-rw-r--r--tests/PHPUnit/Core/HttpTest.php14
-rwxr-xr-xtests/PHPUnit/Core/Unzip/empty.zip0
-rwxr-xr-xtests/PHPUnit/Core/Unzip/test.gzbin41 -> 0 bytes
-rwxr-xr-xtests/PHPUnit/Core/Unzip/test.tar.gzbin202 -> 0 bytes
-rw-r--r--tests/PHPUnit/Core/Unzip/zaabs.zipbin132 -> 0 bytes
-rw-r--r--tests/PHPUnit/Core/Unzip/zaatt.zipbin128 -> 0 bytes
-rw-r--r--tests/PHPUnit/Core/UnzipTest.php217
-rw-r--r--tests/PHPUnit/DatabaseTestCase.php63
-rw-r--r--tests/PHPUnit/Fixture.php9
-rw-r--r--tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorIdAndUserId.php17
-rw-r--r--tests/PHPUnit/Fixtures/SqlDump.php3
-rw-r--r--tests/PHPUnit/Integration/ArchiveInvalidationTest.php1
-rw-r--r--tests/PHPUnit/Integration/Core/AccessTest.php55
-rw-r--r--tests/PHPUnit/Integration/Core/CronArchive/SharedSiteIdsTest.php2
-rw-r--r--tests/PHPUnit/Integration/Core/LogTest.php8
-rw-r--r--tests/PHPUnit/Integration/Core/OptionTest.php1
-rw-r--r--tests/PHPUnit/Integration/Core/SegmentTest.php7
-rw-r--r--tests/PHPUnit/Integration/Core/Tracker/DbTest.php42
-rw-r--r--tests/PHPUnit/Integration/Core/TrackerTest.php5
-rwxr-xr-xtests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php7
-rw-r--r--tests/PHPUnit/Integration/PivotByQueryParamTest.php4
-rw-r--r--tests/PHPUnit/Integration/PrivacyManagerTest.php6
-rw-r--r--tests/PHPUnit/Integration/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml140
-rw-r--r--tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml20
-rw-r--r--tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml20
-rw-r--r--tests/PHPUnit/Integration/expected/test_ImportLogs__Live.getLastVisitsDetails_range.xml80
-rw-r--r--tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisitAsc__Live.getLastVisitsDetails_month.xml28
-rw-r--r--tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml12
-rw-r--r--tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByVisitCount__Live.getLastVisitsDetails_month.xml12
-rw-r--r--tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml12
-rw-r--r--tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml18
-rw-r--r--tests/PHPUnit/Integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Live.getLastVisitsDetails_day.xml4
-rw-r--r--tests/PHPUnit/Integration/expected/test_OneVisitor_NoKeywordSpecified__Live.getLastVisitsDetails_day.xml4
-rw-r--r--tests/PHPUnit/Integration/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml6
-rw-r--r--tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml (renamed from tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_CreatesCorrectPivotTable_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml)0
-rw-r--r--tests/PHPUnit/Integration/expected/test_TimezonesTest__Live.getLastVisitsDetails_day.xml2
-rw-r--r--tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html22
-rw-r--r--tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdfbin509145 -> 510530 bytes
-rw-r--r--tests/PHPUnit/Integration/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml41
-rw-r--r--tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_day.xml10
-rw-r--r--tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_month.xml10
-rw-r--r--tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_week.xml10
-rw-r--r--tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_year.xml10
-rw-r--r--tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml4
-rw-r--r--tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml6
-rw-r--r--tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html22
-rw-r--r--tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdfbin527900 -> 528539 bytes
-rw-r--r--tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml6
-rw-r--r--tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getVisitorProfile.xml4
-rwxr-xr-xtests/PHPUnit/IntegrationTestCase.php8
-rw-r--r--tests/PHPUnit/TestingEnvironment.php2
m---------tests/PHPUnit/UI0
-rw-r--r--tests/PHPUnit/bootstrap.php5
-rw-r--r--tests/PHPUnit/proxy/includes.php4
-rw-r--r--tests/resources/staticFileServer.php6
341 files changed, 5191 insertions, 8416 deletions
diff --git a/.travis.yml b/.travis.yml
index 090a4291b3..b313620187 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -75,12 +75,12 @@ matrix:
env: TEST_SUITE=AngularJSTests MYSQL_ADAPTER=PDO_MYSQL
- php: hhvm
env: TEST_SUITE=AngularJSTests MYSQL_ADAPTER=PDO_MYSQL
- # Only run Mysqli tests on PHP 5.3
- - php: 5.4
+ # Only run Mysqli tests on PHP 5.4
+ - php: 5.3.3
env: TEST_SUITE=IntegrationTests MYSQL_ADAPTER=MYSQLI
- - php: 5.4
+ - php: 5.3.3
env: TEST_SUITE=PluginTests MYSQL_ADAPTER=MYSQLI
- - php: 5.4
+ - php: 5.3.3
env: TEST_SUITE=CoreTests MYSQL_ADAPTER=MYSQLI
- php: 5.5
env: TEST_SUITE=IntegrationTests MYSQL_ADAPTER=MYSQLI
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e6ad088e2..93a02516f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,13 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API'
* The following methods have been added: setPassword, setPasswordHash, getTokenAuthSecret and getLogin.
* Clarifying semantics of each method and what they must support and can support.
* **Read the documentation for the [Auth interface](http://developer.piwik.org/api-reference/Piwik/Auth) to learn more.**
+* The `Piwik\Unzip\*` classes have been extracted out of the Piwik repository into a separate component named [Decompress](https://github.com/piwik/component-decompress).
+ * `Piwik\Unzip` has not moved, it is kept for backward compatibility. If you have been using that class, you don't need to change anything.
+ * The `Piwik\Unzip\*` classes (Tar, PclZip, Gzip, ZipArchive) have moved to the `Piwik\Decompress\*` namespace (inside the new repository).
+ * `Piwik\Unzip\UncompressInterface` has been moved and renamed to `Piwik\Decompress\DecompressInterface` (inside the new repository).
+
+### Deprecations
+* The Piwik::setUserHasSuperUserAccess method is deprecated, instead use Access::doAsSuperUser. This method will ensure that super user access is properly rescinded after the callback finishes.
### New commands
* `generate:angular-directive` Let's you easily generate a template for a new angular directive for any plugin.
diff --git a/composer.json b/composer.json
index be32b796bf..270968a3ba 100644
--- a/composer.json
+++ b/composer.json
@@ -37,7 +37,8 @@
"symfony/console": ">=v2.3.5",
"tedivm/jshrink": "v0.5.1",
"mustangostang/spyc": "0.5.*",
- "piwik/device-detector": "2.*"
+ "piwik/device-detector": "2.*",
+ "piwik/decompress": "~0.1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.1",
diff --git a/composer.lock b/composer.lock
index 47fe2533ba..a8676c511e 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "37a6e1fcbf0ec87f900f82bf83c0804f",
+ "hash": "d9542e577717b3fa1b0fa3d1fee3803b",
"packages": [
{
"name": "leafo/lessphp",
@@ -95,17 +95,52 @@
"time": "2013-02-21 10:52:01"
},
{
+ "name": "piwik/decompress",
+ "version": "0.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/piwik/component-decompress.git",
+ "reference": "f02c7f1d92b33955ce1b44a9623d8467fd2e2c49"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/piwik/component-decompress/zipball/f02c7f1d92b33955ce1b44a9623d8467fd2e2c49",
+ "reference": "f02c7f1d92b33955ce1b44a9623d8467fd2e2c49",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Piwik\\Decompress\\": "src/"
+ },
+ "classmap": [
+ "libs/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0"
+ ],
+ "time": "2014-10-02 22:01:11"
+ },
+ {
"name": "piwik/device-detector",
- "version": "dev-master",
+ "version": "2.4",
"source": {
"type": "git",
"url": "https://github.com/piwik/device-detector.git",
- "reference": "1c7334cd35052b9b7661e13396d4da7a7f47e323"
+ "reference": "64d91375aea2340b81e2cb46745dc3b2213d3231"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/piwik/device-detector/zipball/1c7334cd35052b9b7661e13396d4da7a7f47e323",
- "reference": "1c7334cd35052b9b7661e13396d4da7a7f47e323",
+ "url": "https://api.github.com/repos/piwik/device-detector/zipball/64d91375aea2340b81e2cb46745dc3b2213d3231",
+ "reference": "64d91375aea2340b81e2cb46745dc3b2213d3231",
"shasum": ""
},
"require": {
@@ -139,7 +174,7 @@
"parser",
"useragent"
],
- "time": "2014-09-24 16:59:50"
+ "time": "2014-08-29 18:24:04"
},
{
"name": "symfony/console",
@@ -439,6 +474,56 @@
"time": "2013-11-09 22:30:54"
},
{
+ "name": "pear/archive_tar",
+ "version": "1.3.11",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/pear/Archive_Tar.git",
+ "reference": "23341344e19bbab1056cf2d2773f28cfccf787a3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/23341344e19bbab1056cf2d2773f28cfccf787a3",
+ "reference": "23341344e19bbab1056cf2d2773f28cfccf787a3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=4.3.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "Archive_Tar": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Michiel Rook",
+ "email": "mrook@php.net",
+ "role": "Lead"
+ },
+ {
+ "name": "Vincent Blavet",
+ "email": "vincent@phpconcept.net"
+ },
+ {
+ "name": "Greg Beaver",
+ "email": "greg@chiaraquartet.net"
+ }
+ ],
+ "description": "Tar file management class",
+ "homepage": "https://github.com/pear/Archive_Tar",
+ "keywords": [
+ "archive",
+ "tar"
+ ],
+ "time": "2013-02-09 11:44:32"
+ },
+ {
"name": "phpunit/php-code-coverage",
"version": "2.0.11",
"source": {
@@ -687,16 +772,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "4.2.6",
+ "version": "4.2.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "c28a790620fe30b049bb693be1ef9cd4e0fe906c"
+ "reference": "c3abe5953d1e60a0bf23012b1bc8c4d07f4832d7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c28a790620fe30b049bb693be1ef9cd4e0fe906c",
- "reference": "c28a790620fe30b049bb693be1ef9cd4e0fe906c",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c3abe5953d1e60a0bf23012b1bc8c4d07f4832d7",
+ "reference": "c3abe5953d1e60a0bf23012b1bc8c4d07f4832d7",
"shasum": ""
},
"require": {
@@ -757,7 +842,7 @@
"testing",
"xunit"
],
- "time": "2014-09-14 09:31:24"
+ "time": "2014-09-06 18:38:27"
},
{
"name": "phpunit/phpunit-mock-objects",
@@ -1129,19 +1214,14 @@
"time": "2014-08-31 03:22:04"
}
],
- "aliases": [
-
- ],
+ "aliases": [],
"minimum-stability": "stable",
"stability-flags": {
- "piwik/device-detector": 20,
"facebook/xhprof": 20
},
"prefer-stable": false,
"platform": {
"php": ">=5.3.2"
},
- "platform-dev": [
-
- ]
+ "platform-dev": []
}
diff --git a/core/API/DataTableManipulator.php b/core/API/DataTableManipulator.php
index 69dc62063b..30513daaf3 100644
--- a/core/API/DataTableManipulator.php
+++ b/core/API/DataTableManipulator.php
@@ -141,6 +141,7 @@ abstract class DataTableManipulator
/**
* Extract the API method for loading subtables from the meta data
*
+ * @throws Exception
* @return string
*/
private function getApiMethodForSubtable()
@@ -148,7 +149,7 @@ abstract class DataTableManipulator
if (!$this->apiMethodForSubtable) {
$meta = API::getInstance()->getMetadata('all', $this->apiModule, $this->apiMethod);
- if(empty($meta)) {
+ if (empty($meta)) {
throw new Exception(sprintf(
"The DataTable cannot be manipulated: Metadata for report %s.%s could not be found. You can define the metadata in a hook, see example at: http://developer.piwik.org/api-reference/events#apigetreportmetadata",
$this->apiModule, $this->apiMethod
diff --git a/core/API/DataTableManipulator/Flattener.php b/core/API/DataTableManipulator/Flattener.php
index 211af52d2b..cfee16e6ba 100644
--- a/core/API/DataTableManipulator/Flattener.php
+++ b/core/API/DataTableManipulator/Flattener.php
@@ -127,6 +127,7 @@ class Flattener extends DataTableManipulator
* Remove the flat parameter from the subtable request
*
* @param array $request
+ * @return array
*/
protected function manipulateSubtableRequest($request)
{
diff --git a/core/API/DataTableManipulator/ReportTotalsCalculator.php b/core/API/DataTableManipulator/ReportTotalsCalculator.php
index 1dc3bc85d9..1cbfeeb362 100644
--- a/core/API/DataTableManipulator/ReportTotalsCalculator.php
+++ b/core/API/DataTableManipulator/ReportTotalsCalculator.php
@@ -194,6 +194,7 @@ class ReportTotalsCalculator extends DataTableManipulator
* Make sure to get all rows of the first level table.
*
* @param array $request
+ * @return array
*/
protected function manipulateSubtableRequest($request)
{
diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php
index 668f97bb60..63ae943433 100644
--- a/core/API/DocumentationGenerator.php
+++ b/core/API/DocumentationGenerator.php
@@ -47,8 +47,8 @@ class DocumentationGenerator
if (!empty($prefixUrls)) {
$prefixUrls = 'http://demo.piwik.org/';
}
+
$str = $toc = '';
- $token_auth = "&token_auth=" . Piwik::getCurrentUserTokenAuth();
$parametersToSet = array(
'idSite' => Common::getRequestVar('idSite', 1, 'int'),
'period' => Common::getRequestVar('period', 'day', 'string'),
@@ -57,52 +57,38 @@ class DocumentationGenerator
foreach (Proxy::getInstance()->getMetadata() as $class => $info) {
$moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
+
if (in_array($moduleName, $this->modulesToHide)) {
continue;
}
+
$toc .= "<a href='#$moduleName'>$moduleName</a><br/>";
- $str .= "\n<a name='$moduleName' id='$moduleName'></a><h2>Module " . $moduleName . "</h2>";
- $str .= "<div class='apiDescription'> " . $info['__documentation'] . " </div>";
- foreach ($info as $methodName => $infoMethod) {
- if ($methodName == '__documentation') {
- continue;
- }
- $params = $this->getParametersString($class, $methodName);
- $str .= "\n <div class='apiMethod'>- <b>$moduleName.$methodName </b>" . $params . "";
- $str .= '<small>';
-
- if ($outputExampleUrls) {
- // we prefix all URLs with $prefixUrls
- // used when we include this output in the Piwik official documentation for example
- $str .= "<span class=\"example\">";
- $exampleUrl = $this->getExampleUrl($class, $methodName, $parametersToSet);
- if ($exampleUrl !== false) {
- $lastNUrls = '';
- if (preg_match('/(&period)|(&date)/', $exampleUrl)) {
- $exampleUrlRss = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet);
- $lastNUrls = ", RSS of the last <a target=_blank href='$exampleUrlRss&format=rss$token_auth&translateColumnNames=1'>10 days</a>";
- }
- $exampleUrl = $prefixUrls . $exampleUrl;
- $str .= " [ Example in
- <a target=_blank href='$exampleUrl&format=xml$token_auth'>XML</a>,
- <a target=_blank href='$exampleUrl&format=JSON$token_auth'>Json</a>,
- <a target=_blank href='$exampleUrl&format=Tsv$token_auth&translateColumnNames=1'>Tsv (Excel)</a>
- $lastNUrls
- ]";
- } else {
- $str .= " [ No example available ]";
- }
- $str .= "</span>";
- }
- $str .= '</small>';
- $str .= "</div>\n";
- }
- $str .= '<div style="margin:15px;"><a href="#topApiRef">↑ Back to top</a></div>';
+ $str .= $this->getInterfaceString($moduleName, $class, $info, $parametersToSet, $outputExampleUrls, $prefixUrls);
}
$str = "<h2 id='topApiRef' name='topApiRef'>Quick access to APIs</h2>
$toc
$str";
+
+ return $str;
+ }
+
+ private function getInterfaceString($moduleName, $class, $info, $parametersToSet, $outputExampleUrls, $prefixUrls)
+ {
+ $str = '';
+
+ $str .= "\n<a name='$moduleName' id='$moduleName'></a><h2>Module " . $moduleName . "</h2>";
+ $str .= "<div class='apiDescription'> " . $info['__documentation'] . " </div>";
+ foreach ($info as $methodName => $infoMethod) {
+ if ($methodName == '__documentation') {
+ continue;
+ }
+
+ $str .= $this->getMethodString($moduleName, $class, $parametersToSet, $outputExampleUrls, $prefixUrls, $methodName, $str);
+ }
+
+ $str .= '<div style="margin:15px;"><a href="#topApiRef">↑ Back to top</a></div>';
+
return $str;
}
@@ -241,4 +227,43 @@ class DocumentationGenerator
$sParameters = implode(", ", $asParameters);
return "($sParameters)";
}
+
+ private function getMethodString($moduleName, $class, $parametersToSet, $outputExampleUrls, $prefixUrls, $methodName)
+ {
+ $str = '';
+ $token_auth = "&token_auth=" . Piwik::getCurrentUserTokenAuth();
+
+ $params = $this->getParametersString($class, $methodName);
+ $str .= "\n <div class='apiMethod'>- <b>$moduleName.$methodName </b>" . $params . "";
+ $str .= '<small>';
+
+ if ($outputExampleUrls) {
+ // we prefix all URLs with $prefixUrls
+ // used when we include this output in the Piwik official documentation for example
+ $str .= "<span class=\"example\">";
+ $exampleUrl = $this->getExampleUrl($class, $methodName, $parametersToSet);
+ if ($exampleUrl !== false) {
+ $lastNUrls = '';
+ if (preg_match('/(&period)|(&date)/', $exampleUrl)) {
+ $exampleUrlRss = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet);
+ $lastNUrls = ", RSS of the last <a target=_blank href='$exampleUrlRss&format=rss$token_auth&translateColumnNames=1'>10 days</a>";
+ }
+ $exampleUrl = $prefixUrls . $exampleUrl;
+ $str .= " [ Example in
+ <a target=_blank href='$exampleUrl&format=xml$token_auth'>XML</a>,
+ <a target=_blank href='$exampleUrl&format=JSON$token_auth'>Json</a>,
+ <a target=_blank href='$exampleUrl&format=Tsv$token_auth&translateColumnNames=1'>Tsv (Excel)</a>
+ $lastNUrls
+ ]";
+ } else {
+ $str .= " [ No example available ]";
+ }
+ $str .= "</span>";
+ }
+
+ $str .= '</small>';
+ $str .= "</div>\n";
+
+ return $str;
+ }
}
diff --git a/core/API/Proxy.php b/core/API/Proxy.php
index d3e1fbad3f..daaa5c9f42 100644
--- a/core/API/Proxy.php
+++ b/core/API/Proxy.php
@@ -24,7 +24,7 @@ use ReflectionMethod;
*
* It will also log the performance of API calls (time spent, parameter values, etc.) if logger available
*
- * @method static \Piwik\API\Proxy getInstance()
+ * @method static Proxy getInstance()
*/
class Proxy extends Singleton
{
diff --git a/core/API/Request.php b/core/API/Request.php
index 03b555ad18..64e8e054fa 100644
--- a/core/API/Request.php
+++ b/core/API/Request.php
@@ -70,7 +70,7 @@ use Piwik\Log;
*/
class Request
{
- protected $request = null;
+ private $request = null;
/**
* Converts the supplied request string into an array of query paramater name/value
diff --git a/core/API/ResponseBuilder.php b/core/API/ResponseBuilder.php
index 17d5d488f7..401dccf96c 100644
--- a/core/API/ResponseBuilder.php
+++ b/core/API/ResponseBuilder.php
@@ -18,7 +18,6 @@ use Piwik\DataTable\Filter\PivotByDimension;
use Piwik\DataTable\Renderer;
use Piwik\DataTable\DataTableInterface;
use Piwik\DataTable\Filter\ColumnDelete;
-use Piwik\Piwik;
/**
*/
@@ -158,7 +157,7 @@ class ResponseBuilder
return Renderer::formatValueXml($message);
}
- protected function handleDataTable(DataTableInterface $datatable)
+ private function handleDataTable(DataTableInterface $datatable)
{
$label = $this->getLabelFromRequest($this->request);
@@ -224,7 +223,7 @@ class ResponseBuilder
return $this->apiRenderer->renderDataTable($datatable);
}
- protected function handleArray($array)
+ private function handleArray($array)
{
$firstArray = null;
$firstKey = null;
diff --git a/core/Access.php b/core/Access.php
index 00093e6e4f..578edaa044 100644
--- a/core/Access.php
+++ b/core/Access.php
@@ -8,6 +8,7 @@
*/
namespace Piwik;
+use Exception;
use Piwik\Db;
/**
@@ -33,29 +34,6 @@ use Piwik\Db;
*/
class Access
{
- private static $instance = null;
-
- /**
- * Gets the singleton instance. Creates it if necessary.
- */
- public static function getInstance()
- {
- if (self::$instance == null) {
- self::$instance = new self;
-
- Piwik::postEvent('Access.createAccessSingleton', array(&self::$instance));
- }
- return self::$instance;
- }
-
- /**
- * Sets the singleton instance. For testing purposes.
- */
- public static function setSingletonInstance($instance)
- {
- self::$instance = $instance;
- }
-
/**
* Array of idsites available to the current user, indexed by permission level
* @see getSitesIdWith*()
@@ -100,6 +78,29 @@ class Access
*/
private $auth = null;
+ private static $instance = null;
+
+ /**
+ * Gets the singleton instance. Creates it if necessary.
+ */
+ public static function getInstance()
+ {
+ if (self::$instance == null) {
+ self::$instance = new self;
+
+ Piwik::postEvent('Access.createAccessSingleton', array(&self::$instance));
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Sets the singleton instance. For testing purposes.
+ */
+ public static function setSingletonInstance($instance)
+ {
+ self::$instance = $instance;
+ }
+
/**
* Returns the list of the existing Access level.
* Useful when a given API method requests a given acccess Level.
@@ -146,6 +147,14 @@ class Access
if ($this->hasSuperUserAccess()) {
return $this->reloadAccessSuperUser();
}
+ }
+
+ if ($this->hasSuperUserAccess()) {
+ return $this->reloadAccessSuperUser();
+ }
+
+ // if the Auth wasn't set, we may be in the special case of setSuperUser(), otherwise we fail TODO: docs + review
+ if ($this->auth === null) {
return false;
}
@@ -155,6 +164,7 @@ class Access
if (!$result->wasAuthenticationSuccessful()) {
return false;
}
+
$this->login = $result->getIdentity();
$this->token_auth = $result->getTokenAuth();
@@ -162,21 +172,26 @@ class Access
if ($result->hasSuperUserAccess()) {
return $this->reloadAccessSuperUser();
}
+
// in case multiple calls to API using different tokens, we ensure we reset it as not SU
$this->setSuperUserAccess(false);
// we join with site in case there are rows in access for an idsite that doesn't exist anymore
// (backward compatibility ; before we deleted the site without deleting rows in _access table)
$accessRaw = $this->getRawSitesWithSomeViewAccess($this->login);
+
foreach ($accessRaw as $access) {
$this->idsitesByAccess[$access['access']][] = $access['idsite'];
}
+
return true;
}
public function getRawSitesWithSomeViewAccess($login)
{
- return Db::fetchAll(self::getSqlAccessSite("access, t2.idsite"), $login);
+ $sql = self::getSqlAccessSite("access, t2.idsite");
+
+ return Db::fetchAll($sql, $login);
}
/**
@@ -187,10 +202,11 @@ class Access
*/
public static function getSqlAccessSite($select)
{
- return "SELECT " . $select . "
- FROM " . Common::prefixTable('access') . " as t1
- JOIN " . Common::prefixTable('site') . " as t2 USING (idsite) " .
- " WHERE login = ?";
+ $access = Common::prefixTable('access');
+ $siteTable = Common::prefixTable('site');
+
+ return "SELECT " . $select . " FROM " . $access . " as t1
+ JOIN " . $siteTable . " as t2 USING (idsite) WHERE login = ?";
}
/**
@@ -323,7 +339,9 @@ class Access
if ($this->hasSuperUserAccess()) {
return;
}
+
$idSitesAccessible = $this->getSitesIdWithAdminAccess();
+
if (count($idSitesAccessible) == 0) {
throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAtLeastOneWebsite', array('admin')));
}
@@ -339,7 +357,9 @@ class Access
if ($this->hasSuperUserAccess()) {
return;
}
+
$idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
+
if (count($idSitesAccessible) == 0) {
throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAtLeastOneWebsite', array('view')));
}
@@ -357,8 +377,10 @@ class Access
if ($this->hasSuperUserAccess()) {
return;
}
+
$idSites = $this->getIdSites($idSites);
$idSitesAccessible = $this->getSitesIdWithAdminAccess();
+
foreach ($idSites as $idsite) {
if (!in_array($idsite, $idSitesAccessible)) {
throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAccessWebsite', array("'admin'", $idsite)));
@@ -378,8 +400,10 @@ class Access
if ($this->hasSuperUserAccess()) {
return;
}
+
$idSites = $this->getIdSites($idSites);
$idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
+
foreach ($idSites as $idsite) {
if (!in_array($idsite, $idSitesAccessible)) {
throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAccessWebsite', array("'view'", $idsite)));
@@ -399,11 +423,41 @@ class Access
}
$idSites = Site::getIdSitesFromIdSitesString($idSites);
+
if (empty($idSites)) {
throw new NoAccessException("The parameter 'idSite=' is missing from the request.");
}
+
return $idSites;
}
+
+ /**
+ * Executes a callback with superuser privileges, making sure those privileges are rescinded
+ * before this method exits. Privileges will be rescinded even if an exception is thrown.
+ *
+ * @param callback $function The callback to execute. Should accept no arguments.
+ * @return mixed The result of `$function`.
+ * @throws Exception rethrows any exceptions thrown by `$function`.
+ * @api
+ */
+ public static function doAsSuperUser($function)
+ {
+ $isSuperUser = self::getInstance()->hasSuperUserAccess();
+
+ self::getInstance()->setSuperUserAccess(true);
+
+ try {
+ $result = $function();
+ } catch (Exception $ex) {
+ self::getInstance()->setSuperUserAccess($isSuperUser);
+
+ throw $ex;
+ }
+
+ self::getInstance()->setSuperUserAccess($isSuperUser);
+
+ return $result;
+ }
}
/**
diff --git a/core/Archive.php b/core/Archive.php
index 65b5b91532..7101850c1d 100644
--- a/core/Archive.php
+++ b/core/Archive.php
@@ -197,18 +197,23 @@ class Archive
{
$websiteIds = Site::getIdSitesFromIdSitesString($idSites, $_restrictSitesToLogin);
- $timezone = count($websiteIds) == 1 ? Site::getTimezoneFor($websiteIds[0]) : false;
+ $timezone = false;
+ if (count($websiteIds) == 1) {
+ $timezone = Site::getTimezoneFor($websiteIds[0]);
+ }
if (Period::isMultiplePeriod($strDate, $period)) {
- $oPeriod = PeriodFactory::build($period, $strDate, $timezone);
+ $oPeriod = PeriodFactory::build($period, $strDate, $timezone);
$allPeriods = $oPeriod->getSubperiods();
} else {
- $oPeriod = PeriodFactory::makePeriodFromQueryParams($timezone, $period, $strDate);
+ $oPeriod = PeriodFactory::makePeriodFromQueryParams($timezone, $period, $strDate);
$allPeriods = array($oPeriod);
}
- $segment = new Segment($segment, $websiteIds);
- $idSiteIsAll = $idSites == self::REQUEST_ALL_WEBSITES_FLAG;
+
+ $segment = new Segment($segment, $websiteIds);
+ $idSiteIsAll = $idSites == self::REQUEST_ALL_WEBSITES_FLAG;
$isMultipleDate = Period::isMultiplePeriod($strDate, $period);
+
return Archive::factory($segment, $allPeriods, $websiteIds, $idSiteIsAll, $isMultipleDate, $skipAggregationOfSubTables);
}
@@ -239,9 +244,11 @@ class Archive
{
$forceIndexedBySite = false;
$forceIndexedByDate = false;
+
if ($idSiteIsAll || count($idSites) > 1) {
$forceIndexedBySite = true;
}
+
if (count($periods) > 1 || $isMultipleDate) {
$forceIndexedByDate = true;
}
@@ -265,7 +272,7 @@ class Archive
*
* @param string|array $names One or more archive names, eg, `'nb_visits'`, `'Referrers_distinctKeywords'`,
* etc.
- * @return false|numeric|array `false` if there is no data to return, a single numeric value if we're not querying
+ * @return false|integer|array `false` if there is no data to return, a single numeric value if we're not querying
* for multiple sites/periods, or an array if multiple sites, periods or names are
* queried for.
*/
@@ -406,9 +413,11 @@ class Archive
private function getRequestedPlugins($archiveNames)
{
$result = array();
+
foreach ($archiveNames as $name) {
$result[] = self::getPluginForReport($name);
}
+
return array_unique($result);
}
@@ -436,6 +445,7 @@ class Archive
* @param int|null $idSubtable See {@link getDataTableExpanded()}
* @param bool $skipAggregationOfSubTables Whether or not we should skip the aggregation of all sub-tables and only aggregate parent DataTable.
* @param int|null $depth See {@link getDataTableExpanded()}
+ * @throws \Exception
* @return DataTable|DataTable\Map See {@link getDataTable()} and
* {@link getDataTableExpanded()} for more
* information
@@ -445,9 +455,10 @@ class Archive
{
Piwik::checkUserHasViewAccess($idSite);
- if($skipAggregationOfSubTables && ($expanded || $idSubtable)) {
+ if ($skipAggregationOfSubTables && ($expanded || $idSubtable)) {
throw new \Exception("Not expected to skipAggregationOfSubTables when expanded=1 or idSubtable is set.");
}
+
$archive = Archive::build($idSite, $period, $date, $segment, $_restrictSitesToLogin = false, $skipAggregationOfSubTables);
if ($idSubtable === false) {
$idSubtable = null;
@@ -495,6 +506,7 @@ class Archive
$archiveNames, $archiveDataType, $this->params->getIdSites(), $this->params->getPeriods(), $defaultRow = null);
$archiveIds = $this->getArchiveIds($archiveNames);
+
if (empty($archiveIds)) {
return $result;
}
@@ -532,7 +544,7 @@ class Archive
// figure out which archives haven't been processed (if an archive has been processed,
// then we have the archive IDs in $this->idarchives)
- $doneFlags = array();
+ $doneFlags = array();
$archiveGroups = array();
foreach ($plugins as $plugin) {
$doneFlag = $this->getDoneStringForPlugin($plugin);
@@ -541,7 +553,7 @@ class Archive
if (!isset($this->idarchives[$doneFlag])) {
$archiveGroup = $this->getArchiveGroupOfPlugin($plugin);
- if($archiveGroup == self::ARCHIVE_ALL_PLUGINS_FLAG) {
+ if ($archiveGroup == self::ARCHIVE_ALL_PLUGINS_FLAG) {
$archiveGroup = reset($plugins);
}
$archiveGroups[] = $archiveGroup;
@@ -559,19 +571,7 @@ class Archive
}
}
- // order idarchives by the table month they belong to
- $idArchivesByMonth = array();
- foreach (array_keys($doneFlags) as $doneFlag) {
- if (empty($this->idarchives[$doneFlag])) {
- continue;
- }
-
- foreach ($this->idarchives[$doneFlag] as $dateRange => $idarchives) {
- foreach ($idarchives as $id) {
- $idArchivesByMonth[$dateRange][] = $id;
- }
- }
- }
+ $idArchivesByMonth = $this->getIdArchivesByMonth($doneFlags);
return $idArchivesByMonth;
}
@@ -800,9 +800,29 @@ class Archive
$idArchive = $archiveLoader->prepareArchive($plugin);
- if($idArchive) {
+ if ($idArchive) {
$this->idarchives[$doneFlag][$periodString][] = $idArchive;
}
}
}
+
+ private function getIdArchivesByMonth($doneFlags)
+ {
+ // order idarchives by the table month they belong to
+ $idArchivesByMonth = array();
+
+ foreach (array_keys($doneFlags) as $doneFlag) {
+ if (empty($this->idarchives[$doneFlag])) {
+ continue;
+ }
+
+ foreach ($this->idarchives[$doneFlag] as $dateRange => $idarchives) {
+ foreach ($idarchives as $id) {
+ $idArchivesByMonth[$dateRange][] = $id;
+ }
+ }
+ }
+
+ return $idArchivesByMonth;
+ }
}
diff --git a/core/Archive/DataCollection.php b/core/Archive/DataCollection.php
index 89b435f9dc..efd63cd925 100644
--- a/core/Archive/DataCollection.php
+++ b/core/Archive/DataCollection.php
@@ -188,6 +188,7 @@ class DataCollection
$this->putRowInIndex($result, $indexKeys, $row, $idSite, $period);
}
}
+
return $result;
}
@@ -208,6 +209,7 @@ class DataCollection
$this->dataNames, $this->dataType, $this->sitesId, $this->periods, $this->defaultRow);
$index = $this->getIndexedArray($resultIndices);
+
return $dataTableFactory->make($index, $resultIndices);
}
@@ -249,6 +251,7 @@ class DataCollection
$dataTableFactory->useSubtable($idSubTable);
$index = $this->getIndexedArray($resultIndices);
+
return $dataTableFactory->make($index, $resultIndices);
}
diff --git a/core/Archive/DataTableFactory.php b/core/Archive/DataTableFactory.php
index 41806c62f9..71eaa7a8c1 100644
--- a/core/Archive/DataTableFactory.php
+++ b/core/Archive/DataTableFactory.php
@@ -154,6 +154,7 @@ class DataTableFactory
}
$this->transformMetadata($dataTable);
+
return $dataTable;
}
diff --git a/core/ArchiveProcessor.php b/core/ArchiveProcessor.php
index 2df309d69c..71e90cde23 100644
--- a/core/ArchiveProcessor.php
+++ b/core/ArchiveProcessor.php
@@ -79,12 +79,12 @@ class ArchiveProcessor
/**
* @var \Piwik\DataAccess\ArchiveWriter
*/
- protected $archiveWriter;
+ private $archiveWriter;
/**
* @var \Piwik\DataAccess\LogAggregator
*/
- protected $logAggregator;
+ private $logAggregator;
/**
* @var Archive
@@ -94,14 +94,14 @@ class ArchiveProcessor
/**
* @var Parameters
*/
- protected $params;
+ private $params;
/**
* @var int
*/
- protected $numberOfVisits = false;
+ private $numberOfVisits = false;
- protected $numberOfVisitsConverted = false;
+ private $numberOfVisitsConverted = false;
/**
* If true, unique visitors are not calculated when we are aggregating data for multiple sites.
@@ -125,11 +125,12 @@ class ArchiveProcessor
protected function getArchive()
{
- if(empty($this->archive)) {
+ if (empty($this->archive)) {
$subPeriods = $this->params->getSubPeriods();
- $idSites = $this->params->getIdSites();
+ $idSites = $this->params->getIdSites();
$this->archive = Archive::factory($this->params->getSegment(), $subPeriods, $idSites);
}
+
return $this->archive;
}
@@ -208,6 +209,7 @@ class ArchiveProcessor
if (!is_array($recordNames)) {
$recordNames = array($recordNames);
}
+
$nameToCount = array();
foreach ($recordNames as $recordName) {
$latestUsedTableId = Manager::getInstance()->getMostRecentTableId();
@@ -218,7 +220,7 @@ class ArchiveProcessor
$nameToCount[$recordName]['level0'] = $rowsCount;
$rowsCountRecursive = $rowsCount;
- if($this->isAggregateSubTables()) {
+ if ($this->isAggregateSubTables()) {
$rowsCountRecursive = $table->getRowsCountRecursive();
}
$nameToCount[$recordName]['recursive'] = $rowsCountRecursive;
@@ -271,7 +273,7 @@ class ArchiveProcessor
public function getNumberOfVisits()
{
- if($this->numberOfVisits === false) {
+ if ($this->numberOfVisits === false) {
throw new Exception("visits should have been set here");
}
return $this->numberOfVisits;
@@ -343,7 +345,7 @@ class ArchiveProcessor
*/
protected function aggregateDataTableRecord($name, $columnsAggregationOperation = null, $columnsToRenameAfterAggregation = null)
{
- if($this->isAggregateSubTables()) {
+ if ($this->isAggregateSubTables()) {
// By default we shall aggregate all sub-tables.
$dataTable = $this->getArchive()->getDataTableExpanded($name, $idSubTable = null, $depth = null, $addMetadataSubtableId = false);
} else {
@@ -440,15 +442,18 @@ class ArchiveProcessor
protected function getAggregatedDataTableMap($data, $columnsAggregationOperation)
{
$table = new DataTable();
+
if (!empty($columnsAggregationOperation)) {
$table->setMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME, $columnsAggregationOperation);
}
+
if ($data instanceof DataTable\Map) {
// as $date => $tableToSum
$this->aggregatedDataTableMapsAsOne($data, $table);
} else {
$table->addDataTable($data, $this->isAggregateSubTables());
}
+
return $table;
}
@@ -460,7 +465,7 @@ class ArchiveProcessor
protected function aggregatedDataTableMapsAsOne(Map $map, DataTable $aggregated)
{
foreach ($map->getDataTables() as $tableToAggregate) {
- if($tableToAggregate instanceof Map) {
+ if ($tableToAggregate instanceof Map) {
$this->aggregatedDataTableMapsAsOne($tableToAggregate, $aggregated);
} else {
$aggregated->addDataTable($tableToAggregate, $this->isAggregateSubTables());
@@ -477,6 +482,7 @@ class ArchiveProcessor
if (is_null($columnsToRenameAfterAggregation)) {
$columnsToRenameAfterAggregation = self::$columnsToRenameAfterAggregation;
}
+
foreach ($columnsToRenameAfterAggregation as $oldName => $newName) {
$table->renameColumn($oldName, $newName, $this->isAggregateSubTables());
}
@@ -487,6 +493,7 @@ class ArchiveProcessor
if (!is_array($columns)) {
$columns = array($columns);
}
+
$operationForColumn = $this->getOperationForColumns($columns, $operationToApply);
$dataTable = $this->getArchive()->getDataTableFromNumeric($columns);
@@ -497,7 +504,7 @@ class ArchiveProcessor
}
$rowMetrics = $results->getFirstRow();
- if($rowMetrics === false) {
+ if ($rowMetrics === false) {
$rowMetrics = new Row;
}
$this->enrichWithUniqueVisitorsMetric($rowMetrics);
@@ -510,6 +517,7 @@ class ArchiveProcessor
$metrics[$name] = 0;
}
}
+
return $metrics;
}
diff --git a/core/ArchiveProcessor/Loader.php b/core/ArchiveProcessor/Loader.php
index a4ffa4d12e..b684d8fee1 100644
--- a/core/ArchiveProcessor/Loader.php
+++ b/core/ArchiveProcessor/Loader.php
@@ -10,7 +10,6 @@ namespace Piwik\ArchiveProcessor;
use Piwik\Archive;
use Piwik\ArchiveProcessor;
use Piwik\Config;
-use Piwik\DataAccess\ArchivePurger;
use Piwik\DataAccess\ArchiveSelector;
use Piwik\Date;
use Piwik\Period;
@@ -82,6 +81,7 @@ class Loader
* Prepares the core metrics if needed.
*
* @param $visits
+ * @return array
*/
protected function prepareCoreMetricsArchive($visits, $visitsConverted)
{
@@ -102,12 +102,14 @@ class Loader
$visits = $metrics['nb_visits'];
$visitsConverted = $metrics['nb_visits_converted'];
}
+
return array($visits, $visitsConverted);
}
protected function prepareAllPluginsArchive($visits, $visitsConverted)
{
$pluginsArchiver = new PluginsArchiver($this->params, $this->isArchiveTemporary());
+
if ($this->mustProcessVisitCount($visits)
|| $this->doesRequestedPluginIncludeVisitsSummary()
) {
@@ -115,9 +117,11 @@ class Loader
$visits = $metrics['nb_visits'];
$visitsConverted = $metrics['nb_visits_converted'];
}
+
if ($this->isThereSomeVisits($visits)) {
$pluginsArchiver->callAggregateAllPlugins($visits, $visitsConverted);
}
+
$idArchive = $pluginsArchiver->finalizeArchive();
return array($idArchive, $visits);
@@ -136,11 +140,13 @@ class Loader
{
$period = $this->params->getPeriod()->getLabel();
$debugSetting = 'always_archive_data_period'; // default
+
if ($period == 'day') {
$debugSetting = 'always_archive_data_day';
} elseif ($period == 'range') {
$debugSetting = 'always_archive_data_range';
}
+
return (bool) Config::getInstance()->Debug[$debugSetting];
}
@@ -162,9 +168,11 @@ class Loader
}
$idAndVisits = ArchiveSelector::getArchiveIdAndVisits($this->params, $minDatetimeArchiveProcessedUTC);
+
if (!$idAndVisits) {
return $noArchiveFound;
}
+
return $idAndVisits;
}
@@ -183,19 +191,27 @@ class Loader
// Permanent archive
return $endDateTimestamp;
}
+
+ $dateStart = $this->params->getDateStart();
+ $period = $this->params->getPeriod();
+ $segment = $this->params->getSegment();
+ $site = $this->params->getSite();
+
// Temporary archive
- return Rules::getMinTimeProcessedForTemporaryArchive($this->params->getDateStart(), $this->params->getPeriod(), $this->params->getSegment(), $this->params->getSite());
+ return Rules::getMinTimeProcessedForTemporaryArchive($dateStart, $period, $segment, $site);
}
protected static function determineIfArchivePermanent(Date $dateEnd)
{
$now = time();
$endTimestampUTC = strtotime($dateEnd->getDateEndUTC());
+
if ($endTimestampUTC <= $now) {
// - if the period we are looking for is finished, we look for a ts_archived that
// is greater than the last day of the archive
return $endTimestampUTC;
}
+
return false;
}
@@ -204,6 +220,7 @@ class Loader
if (is_null($this->temporaryArchive)) {
throw new \Exception("getMinTimeArchiveProcessed() should be called prior to isArchiveTemporary()");
}
+
return $this->temporaryArchive;
}
diff --git a/core/ArchiveProcessor/Parameters.php b/core/ArchiveProcessor/Parameters.php
index 79cb1c7196..1528d8210c 100644
--- a/core/ArchiveProcessor/Parameters.php
+++ b/core/ArchiveProcessor/Parameters.php
@@ -91,7 +91,7 @@ class Parameters
*/
public function getSubPeriods()
{
- if($this->getPeriod()->getLabel() == 'day') {
+ if ($this->getPeriod()->getLabel() == 'day') {
return array( $this->getPeriod() );
}
return $this->getPeriod()->getSubperiods();
diff --git a/core/ArchiveProcessor/PluginsArchiver.php b/core/ArchiveProcessor/PluginsArchiver.php
index 10f61287ed..2986d05dd3 100644
--- a/core/ArchiveProcessor/PluginsArchiver.php
+++ b/core/ArchiveProcessor/PluginsArchiver.php
@@ -56,7 +56,7 @@ class PluginsArchiver
*/
public function callAggregateCoreMetrics()
{
- if($this->isSingleSiteDayArchive) {
+ if ($this->isSingleSiteDayArchive) {
$metrics = $this->aggregateDayVisitsMetrics();
} else {
$metrics = $this->aggregateMultipleVisitsMetrics();
@@ -92,11 +92,11 @@ class PluginsArchiver
/** @var Archiver $archiver */
$archiver = new $archiverClass($this->archiveProcessor);
- if(!$archiver->isEnabled()) {
+ if (!$archiver->isEnabled()) {
continue;
}
- if($this->shouldProcessReportsForPlugin($pluginName)) {
- if($this->isSingleSiteDayArchive) {
+ if ($this->shouldProcessReportsForPlugin($pluginName)) {
+ if ($this->isSingleSiteDayArchive) {
$archiver->aggregateDayReport();
} else {
$archiver->aggregateMultipleReports();
diff --git a/core/ArchiveProcessor/Rules.php b/core/ArchiveProcessor/Rules.php
index a64a4f81ad..31b2fc380a 100644
--- a/core/ArchiveProcessor/Rules.php
+++ b/core/ArchiveProcessor/Rules.php
@@ -33,8 +33,6 @@ class Rules
const FLAG_TABLE_PURGED = 'lastPurge_';
- public static $purgeOutdatedArchivesIsDisabled = false;
-
/** Flag that will forcefully disable the archiving process (used in tests only) */
public static $archivingDisabledByTests = false;
@@ -129,16 +127,6 @@ class Rules
return $doneFlags;
}
- public static function disablePurgeOutdatedArchives()
- {
- self::$purgeOutdatedArchivesIsDisabled = true;
- }
-
- public static function enablePurgeOutdatedArchives()
- {
- self::$purgeOutdatedArchivesIsDisabled = false;
- }
-
/**
* Given a monthly archive table, will delete all reports that are now outdated,
* or reports that ended with an error
@@ -148,9 +136,6 @@ class Rules
*/
public static function shouldPurgeOutdatedArchives(Date $date)
{
- if (self::$purgeOutdatedArchivesIsDisabled) {
- return false;
- }
$key = self::FLAG_TABLE_PURGED . "blob_" . $date->toString('Y_m');
$timestamp = Option::get($key);
@@ -222,7 +207,7 @@ class Rules
{
$uiSettingIsEnabled = Controller::isGeneralSettingsAdminEnabled();
- if($uiSettingIsEnabled) {
+ if ($uiSettingIsEnabled) {
$timeToLive = Option::get(self::OPTION_TODAY_ARCHIVE_TTL);
if ($timeToLive !== false) {
return $timeToLive;
@@ -266,7 +251,7 @@ class Rules
{
$uiSettingIsEnabled = Controller::isGeneralSettingsAdminEnabled();
- if($uiSettingIsEnabled) {
+ if ($uiSettingIsEnabled) {
$browserArchivingEnabled = Option::get(self::OPTION_BROWSER_TRIGGER_ARCHIVING);
if ($browserArchivingEnabled !== false) {
return (bool)$browserArchivingEnabled;
diff --git a/core/AssetManager.php b/core/AssetManager.php
index 1badba4d9e..6759facb2b 100644
--- a/core/AssetManager.php
+++ b/core/AssetManager.php
@@ -37,7 +37,7 @@ use Piwik\Translate;
* the global option 'disable_merged_assets'. See the documentation in the global
* config for more information.
*
- * @method static \Piwik\AssetManager getInstance()
+ * @method static AssetManager getInstance()
*/
class AssetManager extends Singleton
{
@@ -72,7 +72,7 @@ class AssetManager extends Singleton
$this->minimalStylesheetFetcher = new StaticUIAssetFetcher(array('plugins/Morpheus/stylesheets/base.less', 'plugins/Morpheus/stylesheets/general/_forms.less'), array(), $this->theme);
$theme = Manager::getInstance()->getThemeEnabled();
- if(!empty($theme)) {
+ if (!empty($theme)) {
$this->theme = new Theme();
}
}
@@ -206,7 +206,7 @@ class AssetManager extends Singleton
$pluginName = $plugin->getPluginName();
$pluginIsCore = Manager::getInstance()->isPluginBundledWithCore($pluginName);
- if(($pluginIsCore && $core) || (!$pluginIsCore && !$core))
+ if (($pluginIsCore && $core) || (!$pluginIsCore && !$core))
$loadedPlugins[] = $pluginName;
}
@@ -220,12 +220,12 @@ class AssetManager extends Singleton
{
$assetsToRemove = array($this->getMergedStylesheetAsset());
- if($pluginName) {
+ if ($pluginName) {
- if($this->pluginContainsJScriptAssets($pluginName)) {
+ if ($this->pluginContainsJScriptAssets($pluginName)) {
PiwikConfig::getInstance()->init();
- if(Manager::getInstance()->isPluginBundledWithCore($pluginName)) {
+ if (Manager::getInstance()->isPluginBundledWithCore($pluginName)) {
$assetsToRemove[] = $this->getMergedCoreJSAsset();
@@ -347,13 +347,13 @@ class AssetManager extends Singleton
$plugin = Manager::getInstance()->getLoadedPlugin($pluginName);
- if($plugin->isTheme()) {
+ if ($plugin->isTheme()) {
$theme = Manager::getInstance()->getTheme($pluginName);
$javaScriptFiles = $theme->getJavaScriptFiles();
- if(!empty($javaScriptFiles))
+ if (!empty($javaScriptFiles))
$assets = array_merge($assets, $javaScriptFiles);
}
diff --git a/core/AssetManager/UIAssetCatalog.php b/core/AssetManager/UIAssetCatalog.php
index d8a45f8964..826259ecb3 100644
--- a/core/AssetManager/UIAssetCatalog.php
+++ b/core/AssetManager/UIAssetCatalog.php
@@ -40,7 +40,7 @@ class UIAssetCatalog
{
$location = $uiAsset->getAbsoluteLocation();
- if(!$this->assetAlreadyInCatalog($location)) {
+ if (!$this->assetAlreadyInCatalog($location)) {
$this->existingAssetLocations[] = $location;
$this->uiAssets[] = $uiAsset;
}
diff --git a/core/AssetManager/UIAssetFetcher.php b/core/AssetManager/UIAssetFetcher.php
index 955ced51a7..3bd34c118e 100644
--- a/core/AssetManager/UIAssetFetcher.php
+++ b/core/AssetManager/UIAssetFetcher.php
@@ -56,7 +56,7 @@ abstract class UIAssetFetcher
*/
public function getCatalog()
{
- if($this->catalog == null)
+ if ($this->catalog == null)
$this->createCatalog();
return $this->catalog;
diff --git a/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php b/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php
index a5ab3d096d..5600068e44 100644
--- a/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php
+++ b/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php
@@ -18,7 +18,7 @@ class JScriptUIAssetFetcher extends UIAssetFetcher
protected function retrieveFileLocations()
{
- if(!empty($this->plugins)) {
+ if (!empty($this->plugins)) {
/**
* Triggered when gathering the list of all JavaScript files needed by Piwik
@@ -53,14 +53,14 @@ class JScriptUIAssetFetcher extends UIAssetFetcher
protected function addThemeFiles()
{
$theme = $this->getTheme();
- if(!$theme) {
+ if (!$theme) {
return;
}
- if(in_array($theme->getThemeName(), $this->plugins)) {
+ if (in_array($theme->getThemeName(), $this->plugins)) {
$jsInThemes = $this->getTheme()->getJavaScriptFiles();
- if(!empty($jsInThemes)) {
+ if (!empty($jsInThemes)) {
foreach($jsInThemes as $jsFile) {
diff --git a/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php b/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php
index 47a78a47ca..47ac94d04b 100644
--- a/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php
+++ b/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php
@@ -18,7 +18,6 @@ class StylesheetUIAssetFetcher extends UIAssetFetcher
$theme = $this->getTheme();
$themeName = $theme->getThemeName();
- $themeName = $this->getTheme()->getThemeName();
$order = array(
'libs/',
'plugins/CoreHome/stylesheets/color_manager.css', // must be before other Piwik stylesheets
@@ -72,7 +71,7 @@ class StylesheetUIAssetFetcher extends UIAssetFetcher
protected function addThemeFiles()
{
$theme = $this->getTheme();
- if(!$theme) {
+ if (!$theme) {
return;
}
$themeStylesheet = $this->getTheme()->getStylesheet();
diff --git a/core/AssetManager/UIAssetMerger.php b/core/AssetManager/UIAssetMerger.php
index 8850de512b..42b5ce0cfe 100644
--- a/core/AssetManager/UIAssetMerger.php
+++ b/core/AssetManager/UIAssetMerger.php
@@ -48,7 +48,7 @@ abstract class UIAssetMerger
public function generateFile()
{
- if(!$this->shouldGenerate())
+ if (!$this->shouldGenerate())
return;
$this->mergedContent = $this->getMergedAssets();
@@ -138,8 +138,9 @@ abstract class UIAssetMerger
*/
private function shouldGenerate()
{
- if(!$this->mergedAsset->exists())
+ if (!$this->mergedAsset->exists()) {
return true;
+ }
return !$this->isFileUpToDate();
}
@@ -162,19 +163,11 @@ abstract class UIAssetMerger
return false;
}
- /**
- * @return boolean
- */
- private function isMergedAssetsDisabled()
- {
- return AssetManager::getInstance()->isMergedAssetsDisabled();
- }
-
private function adjustPaths()
{
$theme = $this->assetFetcher->getTheme();
// During installation theme is not yet ready
- if($theme) {
+ if ($theme) {
$this->mergedContent = $this->assetFetcher->getTheme()->rewriteAssetsPathToTheme($this->mergedContent);
}
}
@@ -189,7 +182,7 @@ abstract class UIAssetMerger
*/
protected function getCacheBusterValue()
{
- if(empty($this->cacheBusterValue))
+ if (empty($this->cacheBusterValue))
$this->cacheBusterValue = $this->generateCacheBuster();
return $this->cacheBusterValue;
@@ -199,12 +192,4 @@ abstract class UIAssetMerger
{
$this->mergedContent = $this->getPreamble() . $this->mergedContent;
}
-
- /**
- * @return boolean
- */
- private function shouldCompareExistingVersion()
- {
- return $this->isMergedAssetsDisabled();
- }
}
diff --git a/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php b/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php
index bfb2442279..935018c580 100644
--- a/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php
+++ b/core/AssetManager/UIAssetMerger/JScriptUIAssetMerger.php
@@ -57,7 +57,7 @@ class JScriptUIAssetMerger extends UIAssetMerger
{
$plugins = $this->getPlugins();
- if(!empty($plugins)) {
+ if (!empty($plugins)) {
/**
* Triggered after all the JavaScript files Piwik uses are minified and merged into a
diff --git a/core/AssetManager/UIAssetMinifier.php b/core/AssetManager/UIAssetMinifier.php
index 5f738092cc..07dd3c769d 100644
--- a/core/AssetManager/UIAssetMinifier.php
+++ b/core/AssetManager/UIAssetMinifier.php
@@ -36,6 +36,7 @@ class UIAssetMinifier extends Singleton
public function isMinifiedJs($content)
{
$lineCount = substr_count($content, "\n");
+
if ($lineCount == 0) {
return true;
}
diff --git a/core/Auth.php b/core/Auth.php
index 5c858010c4..8e3a1e28e9 100644
--- a/core/Auth.php
+++ b/core/Auth.php
@@ -12,20 +12,39 @@ namespace Piwik;
use Exception;
/**
- * Base for authentication implementations. Plugins that provide Auth implementations
- * must provide a class that implements this interface. Additionally, an instance
- * of that class must be set in the {@link \Piwik\Registry} class with the 'auth'
- * key during the {@link Request.initAuthenticationObject} event.
+ * Base interface for authentication implementations.
+ *
+ * Plugins that provide Auth implementations must provide a class that implements
+ * this interface. Additionally, an instance of that class must be set in the
+ * {@link \Piwik\Registry} class with the 'auth' key during the
+ * [Request.initAuthenticationObject](http://developer.piwik.org/api-reference/events#requestinitauthenticationobject)
+ * event.
*
* Authentication implementations must support authentication via username and
* clear-text password and authentication via username and token auth. They can
* additionally support authentication via username and an MD5 hash of a password. If
- * they don't support it, then formless authentication will fail.
+ * they don't support it, then [formless authentication](http://piwik.org/faq/how-to/faq_30/) will fail.
*
* Derived implementations should favor authenticating by password over authenticating
* by token auth. That is to say, if a token auth and a password are set, password
* authentication should be used.
*
+ * ### Examples
+ *
+ * **How an Auth implementation will be used**
+ *
+ * // authenticating by password
+ * $auth = \Piwik\Registry::get('auth');
+ * $auth->setLogin('user');
+ * $auth->setPassword('password');
+ * $result = $auth->authenticate();
+ *
+ * // authenticating by token auth
+ * $auth = \Piwik\Registry::get('auth');
+ * $auth->setLogin('user');
+ * $auth->setTokenAuth('...');
+ * $result = $auth->authenticate();
+ *
* @api
*/
interface Auth
@@ -59,7 +78,7 @@ interface Auth
* uses an MD5 hash of a user's password.
*
* @return string
- * @throws Exception if the token auth cannot be calculated at the current time.
+ * @throws Exception if the token auth secret does not exist or cannot be obtained.
*/
public function getTokenAuthSecret();
@@ -95,6 +114,9 @@ interface Auth
* {@link Piwik\Plugins\Login\SessionInitializer::getHashTokenAuth()} method.
*
* @return AuthResult
+ * @throws Exception if the Auth implementation has an invalid state (ie, no login
+ * was specified). Note: implementations are not **required** to throw
+ * exceptions for invalid state, but they are allowed to.
*/
public function authenticate();
}
diff --git a/core/BaseFactory.php b/core/BaseFactory.php
index 6f09f952ef..0bc8716eb2 100644
--- a/core/BaseFactory.php
+++ b/core/BaseFactory.php
@@ -9,7 +9,6 @@
namespace Piwik;
use Exception;
-use Piwik\Common;
/**
* Base class for all factory types.
diff --git a/core/CacheFile.php b/core/CacheFile.php
index 9eb0587595..48a23004bf 100644
--- a/core/CacheFile.php
+++ b/core/CacheFile.php
@@ -25,11 +25,7 @@ class CacheFile
/**
* @var string
*/
- protected $cachePath;
- /**
- * @var
- */
- protected $cachePrefix;
+ private $cachePath;
/**
* Minimum enforced TTL in seconds
@@ -67,11 +63,12 @@ class CacheFile
if (empty($id)) {
return false;
}
+
$id = $this->cleanupId($id);
$cache_complete = false;
- $content = '';
- $expires_on = false;
+ $content = '';
+ $expires_on = false;
// We are assuming that most of the time cache will exists
$cacheFilePath = $this->cachePath . $id . '.php';
@@ -88,6 +85,7 @@ class CacheFile
) {
return false;
}
+
return $content;
}
@@ -104,6 +102,7 @@ class CacheFile
if (!Filesystem::isValidFilename($id)) {
throw new Exception("Invalid cache ID request $id");
}
+
return $id;
}
@@ -120,25 +119,23 @@ class CacheFile
if (empty($id)) {
return false;
}
+
if (!is_dir($this->cachePath)) {
Filesystem::mkdir($this->cachePath);
}
+
if (!is_writable($this->cachePath)) {
return false;
}
- $id = $this->cleanupId($id);
+ $id = $this->cleanupId($id);
$id = $this->cachePath . $id . '.php';
if (is_object($content)) {
throw new \Exception('You cannot use the CacheFile to cache an object, only arrays, strings and numbers.');
}
- $cache_literal = "<" . "?php\n";
- $cache_literal .= "$" . "content = " . var_export($content, true) . ";\n";
- $cache_literal .= "$" . "expires_on = " . $this->getExpiresTime() . ";\n";
- $cache_literal .= "$" . "cache_complete = true;\n";
- $cache_literal .= "?" . ">";
+ $cache_literal = $this->buildCacheLiteral($content);
// Write cache to a temp file, then rename it, overwriting the old cache
// On *nix systems this should guarantee atomicity
@@ -162,6 +159,7 @@ class CacheFile
return true;
}
+
return false;
}
@@ -176,14 +174,17 @@ class CacheFile
if (empty($id)) {
return false;
}
+
$id = $this->cleanupId($id);
$filename = $this->cachePath . $id . '.php';
+
if (file_exists($filename)) {
$this->opCacheInvalidate($filename);
@unlink($filename);
return true;
}
+
return false;
}
@@ -218,8 +219,19 @@ class CacheFile
@opcache_invalidate($filepath, $force = true);
}
if (function_exists('apc_delete_file')) {
- apc_delete_file($filepath);
+ @apc_delete_file($filepath);
}
}
}
+
+ private function buildCacheLiteral($content)
+ {
+ $cache_literal = "<" . "?php\n";
+ $cache_literal .= "$" . "content = " . var_export($content, true) . ";\n";
+ $cache_literal .= "$" . "expires_on = " . $this->getExpiresTime() . ";\n";
+ $cache_literal .= "$" . "cache_complete = true;\n";
+ $cache_literal .= "?" . ">";
+
+ return $cache_literal;
+ }
}
diff --git a/core/CliMulti.php b/core/CliMulti.php
index 21e0ee0b0d..6de96af5b0 100644
--- a/core/CliMulti.php
+++ b/core/CliMulti.php
@@ -58,13 +58,15 @@ class CliMulti {
public function request(array $piwikUrls)
{
$chunks = array($piwikUrls);
- if($this->concurrentProcessesLimit) {
+ if ($this->concurrentProcessesLimit) {
$chunks = array_chunk( $piwikUrls, $this->concurrentProcessesLimit);
}
+
$results = array();
foreach($chunks as $urlsChunk) {
$results = array_merge($results, $this->requestUrls($urlsChunk));
}
+
return $results;
}
@@ -89,17 +91,22 @@ class CliMulti {
private function start($piwikUrls)
{
foreach ($piwikUrls as $index => $url) {
- $cmdId = $this->generateCommandId($url) . $index;
- $output = new Output($cmdId);
+ $cmdId = $this->generateCommandId($url) . $index;
+ $this->executeUrlCommand($cmdId, $url);
+ }
+ }
- if ($this->supportsAsync) {
- $this->executeAsyncCli($url, $output, $cmdId);
- } else {
- $this->executeNotAsyncHttp($url, $output);
- }
+ private function executeUrlCommand($cmdId, $url)
+ {
+ $output = new Output($cmdId);
- $this->outputs[] = $output;
+ if ($this->supportsAsync) {
+ $this->executeAsyncCli($url, $output, $cmdId);
+ } else {
+ $this->executeNotAsyncHttp($url, $output);
}
+
+ $this->outputs[] = $output;
}
private function buildCommand($hostname, $query, $outputFile)
@@ -192,7 +199,7 @@ class CliMulti {
$timeOneWeekAgo = strtotime('-1 week');
$files = _glob(self::getTmpPath() . '/*');
- if(empty($files)) {
+ if (empty($files)) {
return;
}
diff --git a/core/CliMulti/CliPhp.php b/core/CliMulti/CliPhp.php
index df82e05376..9ee5b191b1 100644
--- a/core/CliMulti/CliPhp.php
+++ b/core/CliMulti/CliPhp.php
@@ -18,12 +18,12 @@ class CliPhp
{
if (defined('PHP_BINARY')) {
- if($this->isValidPhpType(PHP_BINARY)) {
- return PHP_BINARY . ' -q';
+ if ($this->isHhvmBinary(PHP_BINARY)) {
+ return PHP_BINARY . ' --php';
}
- if($this->isHhvmBinary(PHP_BINARY)) {
- return PHP_BINARY . ' --php';
+ if ($this->isValidPhpType(PHP_BINARY)) {
+ return PHP_BINARY . ' -q';
}
}
diff --git a/core/CliMulti/Process.php b/core/CliMulti/Process.php
index e8dfb662a4..15dc539e59 100644
--- a/core/CliMulti/Process.php
+++ b/core/CliMulti/Process.php
@@ -154,7 +154,7 @@ class Process
return false;
}
- if(!self::isProcFSMounted()) {
+ if (!self::isProcFSMounted()) {
return false;
}
@@ -169,11 +169,11 @@ class Process
{
$uname = @shell_exec('uname -a');
- if(empty($uname)) {
+ if (empty($uname)) {
$uname = php_uname();
}
- if(strpos($uname, 'synology') !== false) {
+ if (strpos($uname, 'synology') !== false) {
return true;
}
return false;
@@ -208,12 +208,12 @@ class Process
*/
private static function isProcFSMounted()
{
- if(is_resource(@fopen('/proc', 'r'))) {
+ if (is_resource(@fopen('/proc', 'r'))) {
return true;
}
// Testing if /proc is a resource with @fopen fails on systems with open_basedir set.
// by using stat we not only test the existance of /proc but also confirm it's a 'proc' filesystem
- $type = shell_exec('stat -f -c "%T" /proc 2>/dev/null');
+ $type = @shell_exec('stat -f -c "%T" /proc 2>/dev/null');
return strpos($type, 'proc') === 0;
}
diff --git a/core/Columns/Updater.php b/core/Columns/Updater.php
index 2dd6e4040e..1bc53607ca 100644
--- a/core/Columns/Updater.php
+++ b/core/Columns/Updater.php
@@ -87,17 +87,17 @@ class Updater extends \Piwik\Updates
$changingColumns = array();
- foreach (VisitDimension::getAllDimensions() as $dimension) {
+ foreach (self::getVisitDimensions() as $dimension) {
$updates = self::getUpdatesForDimension($dimension, 'log_visit.', $visitColumns, $conversionColumns);
$changingColumns = self::mixinUpdates($changingColumns, $updates);
}
- foreach (ActionDimension::getAllDimensions() as $dimension) {
+ foreach (self::getActionDimensions() as $dimension) {
$updates = self::getUpdatesForDimension($dimension, 'log_link_visit_action.', $actionColumns);
$changingColumns = self::mixinUpdates($changingColumns, $updates);
}
- foreach (ConversionDimension::getAllDimensions() as $dimension) {
+ foreach (self::getConversionDimensions() as $dimension) {
$updates = self::getUpdatesForDimension($dimension, 'log_conversion.', $conversionColumns);
$changingColumns = self::mixinUpdates($changingColumns, $updates);
}
@@ -167,15 +167,15 @@ class Updater extends \Piwik\Updates
$actionColumns = DbHelper::getTableColumns(Common::prefixTable('log_link_visit_action'));
$conversionColumns = DbHelper::getTableColumns(Common::prefixTable('log_conversion'));
- foreach (VisitDimension::getAllDimensions() as $dimension) {
+ foreach (self::getVisitDimensions() as $dimension) {
$versions = self::mixinVersions($dimension, 'log_visit.', $visitColumns, $versions);
}
- foreach (ActionDimension::getAllDimensions() as $dimension) {
+ foreach (self::getActionDimensions() as $dimension) {
$versions = self::mixinVersions($dimension, 'log_link_visit_action.', $actionColumns, $versions);
}
- foreach (ConversionDimension::getAllDimensions() as $dimension) {
+ foreach (self::getConversionDimensions() as $dimension) {
$versions = self::mixinVersions($dimension, 'log_conversion.', $conversionColumns, $versions);
}
@@ -327,4 +327,25 @@ class Updater extends \Piwik\Updates
return array();
}
+
+ private static function getVisitDimensions()
+ {
+ return VisitDimension::getAllDimensions();
+ }
+
+ /**
+ * @return mixed|Dimension[]
+ */
+ private static function getActionDimensions()
+ {
+ return ActionDimension::getAllDimensions();
+ }
+
+ /**
+ * @return mixed|Dimension[]
+ */
+ private static function getConversionDimensions()
+ {
+ return ConversionDimension::getAllDimensions();
+ }
}
diff --git a/core/Common.php b/core/Common.php
index d9ddf0be25..5c1391bfdb 100644
--- a/core/Common.php
+++ b/core/Common.php
@@ -362,10 +362,13 @@ class Common
*/
private static function undoMagicQuotes($value)
{
- return version_compare(PHP_VERSION, '5.4', '<')
- && get_magic_quotes_gpc()
- ? stripslashes($value)
- : $value;
+ if (version_compare(PHP_VERSION, '5.4', '<') &&
+ get_magic_quotes_gpc()) {
+
+ $value = stripslashes($value);
+ }
+
+ return $value;
}
/**
@@ -375,8 +378,7 @@ class Common
*/
public static function sanitizeLineBreaks($value)
{
- $value = str_replace(array("\n", "\r", "\0"), '', $value);
- return $value;
+ return str_replace(array("\n", "\r", "\0"), '', $value);
}
/**
@@ -406,6 +408,7 @@ class Common
if (is_null($requestArrayToUse)) {
$requestArrayToUse = $_GET + $_POST;
}
+
$varDefault = self::sanitizeInputValues($varDefault);
if ($varType === 'int') {
// settype accepts only integer
@@ -469,6 +472,7 @@ class Common
}
settype($value, $varType);
}
+
return $value;
}
@@ -496,14 +500,17 @@ class Common
public static function hash($str, $raw_output = false)
{
static $hashAlgorithm = null;
+
if (is_null($hashAlgorithm)) {
$hashAlgorithm = @Config::getInstance()->General['hash_algorithm'];
}
if ($hashAlgorithm) {
$hash = @hash($hashAlgorithm, $str, $raw_output);
- if ($hash !== false)
+ if ($hash !== false) {
+
return $hash;
+ }
}
return md5($str, $raw_output);
@@ -520,7 +527,7 @@ class Common
public static function getRandomString($length = 16, $alphabet = "abcdefghijklmnoprstuvwxyz0123456789")
{
$chars = $alphabet;
- $str = '';
+ $str = '';
list($usec, $sec) = explode(" ", microtime());
$seed = ((float)$sec + (float)$usec) * 100000;
@@ -530,6 +537,7 @@ class Common
$rand_key = mt_rand(0, strlen($chars) - 1);
$str .= substr($chars, $rand_key, 1);
}
+
return str_shuffle($str);
}
@@ -571,6 +579,7 @@ class Common
) {
throw new Exception("visitorId is expected to be a " . Tracker::LENGTH_HEX_ID_STRING . " hex char string");
}
+
return self::hex2bin($id);
}
@@ -584,6 +593,7 @@ class Common
{
require_once PIWIK_INCLUDE_PATH . '/libs/PiwikTracker/PiwikTracker.php';
$userIdHashed = \PiwikTracker::getUserIdHashed($userId);
+
return self::convertVisitorIdToBin($userIdHashed);
}
@@ -730,6 +740,7 @@ class Common
if ($includeInternalCodes) {
return array_merge($countriesList, $extras);
}
+
return $countriesList;
}
@@ -1054,7 +1065,7 @@ class Common
public static function sendHeader($header, $replace = true)
{
// don't send header in CLI mode
- if(!Common::isPhpCliMode() and !headers_sent()) {
+ if (!Common::isPhpCliMode() and !headers_sent()) {
header($header, $replace);
}
}
diff --git a/core/Config.php b/core/Config.php
index 9d6326bb2e..749c585647 100644
--- a/core/Config.php
+++ b/core/Config.php
@@ -36,7 +36,7 @@ use Exception;
* Config::getInstance()->MySection = array('myoption' => 1);
* Config::getInstance()->forceSave();
*
- * @method static \Piwik\Config getInstance()
+ * @method static Config getInstance()
*/
class Config extends Singleton
{
@@ -416,9 +416,9 @@ class Config extends Singleton
$section = $this->getFromGlobalConfig($name);
$sectionCommon = $this->getFromCommonConfig($name);
- if(empty($section) && !empty($sectionCommon)) {
+ if (empty($section) && !empty($sectionCommon)) {
$section = $sectionCommon;
- } elseif(!empty($section) && !empty($sectionCommon)) {
+ } elseif (!empty($section) && !empty($sectionCommon)) {
$section = $this->array_merge_recursive_distinct($section, $sectionCommon);
}
@@ -459,6 +459,7 @@ class Config extends Singleton
$user['bridge'] = 1;
return $user;
}
+
} catch (Exception $e) {}
return array();
@@ -559,7 +560,7 @@ class Config extends Singleton
}
// If there is a common.config.ini.php, this will ensure config.ini.php does not duplicate its values
- if(!empty($configCommon)) {
+ if (!empty($configCommon)) {
$configGlobal = $this->array_merge_recursive_distinct($configGlobal, $configCommon);
}
diff --git a/core/Console.php b/core/Console.php
index 03f9df438d..423c06d771 100644
--- a/core/Console.php
+++ b/core/Console.php
@@ -40,6 +40,7 @@ class Console extends Application
{
$this->initPiwikHost($input);
$this->initConfig($output);
+
try {
self::initPlugins();
} catch(\Exception $e) {
@@ -51,16 +52,24 @@ class Console extends Application
$commands = $this->getAvailableCommands();
foreach ($commands as $command) {
- if (!class_exists($command)) {
- Log::warning(sprintf('Cannot add command %s, class does not exist', $command));
- } elseif (!is_subclass_of($command, 'Piwik\Plugin\ConsoleCommand')) {
- Log::warning(sprintf('Cannot add command %s, class does not extend Piwik\Plugin\ConsoleCommand', $command));
- } else {
- $this->add(new $command);
- }
+ $this->addCommandIfExists($command);
}
- return parent::doRun($input, $output);
+ $self = $this;
+ return Access::doAsSuperUser(function () use ($input, $output, $self) {
+ return call_user_func(array($self, 'Symfony\Component\Console\Application::doRun'), $input, $output);
+ });
+ }
+
+ private function addCommandIfExists($command)
+ {
+ if (!class_exists($command)) {
+ Log::warning(sprintf('Cannot add command %s, class does not exist', $command));
+ } elseif (!is_subclass_of($command, 'Piwik\Plugin\ConsoleCommand')) {
+ Log::warning(sprintf('Cannot add command %s, class does not extend Piwik\Plugin\ConsoleCommand', $command));
+ } else {
+ $this->add(new $command);
+ }
}
/**
@@ -128,9 +137,11 @@ class Console extends Application
protected function initConfig(OutputInterface $output)
{
$config = Config::getInstance();
+
try {
$config->checkLocalConfigFound();
return $config;
+
} catch (\Exception $e) {
$output->writeln($e->getMessage() . "\n");
}
@@ -151,6 +162,8 @@ class Console extends Application
$extra = new \Piwik\Plugins\EnterpriseAdmin\EnterpriseAdmin();
$extra->addConsoleCommands($commands);
}
+
return $commands;
}
+
}
diff --git a/core/Cookie.php b/core/Cookie.php
index d081ec944e..993d97f1e2 100644
--- a/core/Cookie.php
+++ b/core/Cookie.php
@@ -200,12 +200,14 @@ class Cookie
private function extractSignedContent($content)
{
$signature = substr($content, -40);
+
if (substr($content, -43, 3) == self::VALUE_SEPARATOR . '_=' &&
$signature == sha1(substr($content, 0, -40) . SettingsPiwik::getSalt())
) {
// strip trailing: VALUE_SEPARATOR '_=' signature"
return substr($content, 0, -43);
}
+
return false;
}
@@ -218,6 +220,7 @@ class Cookie
protected function loadContentFromCookie()
{
$cookieStr = $this->extractSignedContent($_COOKIE[$this->name]);
+
if ($cookieStr === false) {
return;
}
@@ -255,6 +258,7 @@ class Cookie
protected function generateContentString()
{
$cookieStr = '';
+
foreach ($this->value as $name => $value) {
if (!is_numeric($value)) {
$value = base64_encode(safe_serialize($value));
@@ -335,6 +339,7 @@ class Cookie
$this->value[$name] = $value;
return;
}
+
$this->value[$this->keyStore][$name] = $value;
}
@@ -347,14 +352,19 @@ class Cookie
public function get($name)
{
$name = self::escapeValue($name);
- if ($this->keyStore === false) {
- return isset($this->value[$name])
- ? self::escapeValue($this->value[$name])
- : false;
+ if (false === $this->keyStore) {
+ if (isset($this->value[$name])) {
+ return self::escapeValue($this->value[$name]);
+ }
+
+ return false;
+ }
+
+ if (isset($this->value[$this->keyStore][$name])) {
+ return self::escapeValue($this->value[$this->keyStore][$name]);
}
- return isset($this->value[$this->keyStore][$name])
- ? self::escapeValue($this->value[$this->keyStore][$name])
- : false;
+
+ return false;
}
/**
@@ -364,8 +374,9 @@ class Cookie
*/
public function __toString()
{
- $str = 'COOKIE ' . $this->name . ', rows count: ' . count($this->value) . ', cookie size = ' . strlen($this->generateContentString()) . " bytes\n";
+ $str = 'COOKIE ' . $this->name . ', rows count: ' . count($this->value) . ', cookie size = ' . strlen($this->generateContentString()) . " bytes\n";
$str .= var_export($this->value, $return = true);
+
return $str;
}
diff --git a/core/CronArchive.php b/core/CronArchive.php
index 442480051b..47fbd7d3df 100644
--- a/core/CronArchive.php
+++ b/core/CronArchive.php
@@ -181,6 +181,13 @@ class CronArchive
*/
public $concurrentRequestsPerWebsite = false;
+ private $websitesWithVisitsSinceLastRun = 0;
+ private $skippedPeriodsArchivesWebsite = 0;
+ private $skippedDayArchivesWebsites = 0;
+ private $skipped = 0;
+ private $processed = 0;
+ private $archivedPeriodsArchivesWebsite = 0;
+
/**
* Returns the option name of the option that stores the time core:archive was last executed.
*
@@ -212,10 +219,13 @@ class CronArchive
*/
public function main()
{
- $this->init();
- $this->run();
- $this->runScheduledTasks();
- $this->end();
+ $self = $this;
+ Access::doAsSuperUser(function () use ($self) {
+ $self->init();
+ $self->run();
+ $self->runScheduledTasks();
+ $self->end();
+ });
}
public function init()
@@ -225,7 +235,6 @@ class CronArchive
$this->initTokenAuth();
$this->initCheckCli();
$this->initStateFromParameters();
- Piwik::setUserHasSuperUserAccess(true);
$this->logInitInfo();
$this->checkPiwikUrlIsValid();
@@ -234,10 +243,10 @@ class CronArchive
// record archiving start time
Option::set(self::OPTION_ARCHIVING_STARTED_TS, time());
- $this->segments = $this->initSegmentsToArchive();
+ $this->segments = $this->initSegmentsToArchive();
$this->allWebsites = APISitesManager::getInstance()->getAllSitesId();
- if(!empty($this->shouldArchiveOnlySpecificPeriods)) {
+ if (!empty($this->shouldArchiveOnlySpecificPeriods)) {
$this->log("- Will process the following periods: " . implode(", ", $this->shouldArchiveOnlySpecificPeriods) . " (--force-periods)");
}
@@ -279,13 +288,6 @@ class CronArchive
$this->runScheduledTasks();
}
- private $websitesWithVisitsSinceLastRun = 0;
- private $skippedPeriodsArchivesWebsite = 0;
- private $skippedDayArchivesWebsites = 0;
- private $skipped = 0;
- private $processed = 0;
- private $archivedPeriodsArchivesWebsite = 0;
-
/**
* Main function, runs archiving on all websites with new activity
*/
@@ -310,7 +312,7 @@ class CronArchive
}
$skipWebsiteForced = in_array($idSite, $this->shouldSkipSpecifiedSites);
- if($skipWebsiteForced) {
+ if ($skipWebsiteForced) {
$this->log("Skipped website id $idSite, found in --skip-idsites ");
$this->skipped++;
continue;
@@ -380,6 +382,7 @@ class CronArchive
// do not logError since errors are already in stderr
$this->log("Error: " . $error);
}
+
$summary = count($this->errors) . " total errors during this script execution, please investigate and try and fix these errors.";
$this->logFatalError($summary);
}
@@ -402,9 +405,11 @@ class CronArchive
$this->log("Starting Scheduled tasks... ");
$tasksOutput = $this->request("?module=API&method=CoreAdminHome.runScheduledTasks&format=csv&convertToUnicode=0&token_auth=" . $this->token_auth);
+
if ($tasksOutput == \Piwik\DataTable\Renderer\Csv::NO_DATA_AVAILABLE) {
$tasksOutput = " No task to run";
}
+
$this->log($tasksOutput);
$this->log("done");
$this->logSection("");
@@ -415,6 +420,7 @@ class CronArchive
$timerWebsite = new Timer;
$lastTimestampWebsiteProcessedPeriods = $lastTimestampWebsiteProcessedDay = false;
+
if ($this->archiveAndRespectTTL) {
Option::clearCachedOption($this->lastRunKey($idSite, "periods"));
$lastTimestampWebsiteProcessedPeriods = Option::get($this->lastRunKey($idSite, "periods"));
@@ -433,6 +439,7 @@ class CronArchive
if ($this->processPeriodsMaximumEverySeconds > 10 * 60) {
$secondsSinceLastExecution += 5 * 60;
}
+
$shouldArchivePeriods = $secondsSinceLastExecution > $this->processPeriodsMaximumEverySeconds;
if (empty($lastTimestampWebsiteProcessedPeriods)) {
// 2) OR always if script never executed for this website before
@@ -455,7 +462,7 @@ class CronArchive
}
$websiteIdIsForced = in_array($idSite, $this->shouldArchiveSpecifiedSites);
- if($websiteIdIsForced) {
+ if ($websiteIdIsForced) {
$shouldArchivePeriods = true;
}
@@ -492,7 +499,7 @@ class CronArchive
}
$shouldProceed = $this->processArchiveDays($idSite, $lastTimestampWebsiteProcessedDay, $shouldArchivePeriods, $timerWebsite);
- if(!$shouldProceed) {
+ if (!$shouldProceed) {
return false;
}
@@ -508,7 +515,7 @@ class CronArchive
$success = true;
foreach (array('week', 'month', 'year') as $period) {
- if(!$this->shouldProcessPeriod($period)) {
+ if (!$this->shouldProcessPeriod($period)) {
// if any period was skipped, we do not mark the Periods archiving as successful
$success = false;
continue;
@@ -521,6 +528,7 @@ class CronArchive
if ($success) {
Option::set($this->lastRunKey($idSite, "periods"), time());
}
+
$this->archivedPeriodsArchivesWebsite++;
$requestsWebsite = $this->requests - $requestsBefore;
@@ -568,9 +576,11 @@ class CronArchive
private function initSegmentsToArchive()
{
$segments = \Piwik\SettingsPiwik::getKnownSegmentsToArchive();
+
if (empty($segments)) {
return array();
}
+
$this->log("- Will pre-process " . count($segments) . " Segments for each website and each period: " . implode(", ", $segments));
return $segments;
}
@@ -603,7 +613,7 @@ class CronArchive
// when some data was purged from this website
// we make sure we query all previous days/weeks/months
$processDaysSince = $lastTimestampWebsiteProcessedDay;
- if($this->isOldReportInvalidatedForWebsite($idSite)
+ if ($this->isOldReportInvalidatedForWebsite($idSite)
// when --force-all-websites option,
// also forces to archive last52 days to be safe
|| $this->shouldArchiveAllSites) {
@@ -634,7 +644,7 @@ class CronArchive
$this->processed++;
// If there is no visit today and we don't need to process this website, we can skip remaining archives
- if ($visitsToday == 0
+ if (0 == $visitsToday
&& !$shouldArchivePeriods
) {
$this->log("Skipped website id $idSite, no visit today, " . $timerWebsite->__toString());
@@ -642,7 +652,7 @@ class CronArchive
return false;
}
- if ($visitsLastDays == 0
+ if (0 == $visitsLastDays
&& !$shouldArchivePeriods
&& $this->shouldArchiveAllSites
) {
@@ -662,7 +672,7 @@ class CronArchive
private function getSegmentsForSite($idSite)
{
$segmentsAllSites = $this->segments;
- $segmentsThisSite = \Piwik\SettingsPiwik::getKnownSegmentsToArchiveForSite($idSite);
+ $segmentsThisSite = SettingsPiwik::getKnownSegmentsToArchiveForSite($idSite);
if (!empty($segmentsThisSite)) {
$this->log("Will pre-process the following " . count($segmentsThisSite) . " Segments for this website (id = $idSite): " . implode(", ", $segmentsThisSite));
}
@@ -731,7 +741,7 @@ class CronArchive
}
// we have already logged the daily archive above
- if($period != "day") {
+ if ($period != "day") {
$this->logArchivedWebsite($idSite, $period, $date, $visitsInLastPeriods, $visitsLastPeriod, $timer);
}
@@ -744,7 +754,7 @@ class CronArchive
private function logSection($title = "")
{
$this->log("---------------------------");
- if(!empty($title)) {
+ if (!empty($title)) {
$this->log($title);
}
}
@@ -788,7 +798,7 @@ class CronArchive
{
$url = $this->piwikUrl . $url . self::APPEND_TO_API_REQUEST;
- if($this->shouldStartProfiler) {
+ if ($this->shouldStartProfiler) {
$url .= "&xhprof=2";
}
@@ -851,6 +861,7 @@ class CronArchive
if (Common::isPhpCliMode()) {
return;
}
+
$token_auth = Common::getRequestVar('token_auth', '', 'string');
if ($token_auth !== $this->token_auth
|| strlen($token_auth) != 32
@@ -892,7 +903,7 @@ class CronArchive
$this->shouldArchiveOnlySitesWithTrafficSince = $this->isShouldArchiveAllSitesWithTrafficSince();
$this->shouldArchiveOnlySpecificPeriods = $this->getPeriodsToProcess();
- if($this->shouldArchiveOnlySitesWithTrafficSince === false) {
+ if ($this->shouldArchiveOnlySitesWithTrafficSince === false) {
// force-all-periods is not set here
if (empty($this->lastSuccessRunTimestamp)) {
// First time we run the script
@@ -905,7 +916,7 @@ class CronArchive
// force-all-periods is set here
$this->archiveAndRespectTTL = false;
- if($this->shouldArchiveOnlySitesWithTrafficSince === true) {
+ if ($this->shouldArchiveOnlySitesWithTrafficSince === true) {
// force-all-periods without value
$this->shouldArchiveOnlySitesWithTrafficSince = self::ARCHIVE_SITES_WITH_TRAFFIC_SINCE;
}
@@ -935,7 +946,7 @@ class CronArchive
*/
public function initWebsiteIds()
{
- if(count($this->shouldArchiveSpecifiedSites) > 0) {
+ if (count($this->shouldArchiveSpecifiedSites) > 0) {
$this->log("- Will process " . count($this->shouldArchiveSpecifiedSites) . " websites (--force-idsites)");
return $this->shouldArchiveSpecifiedSites;
@@ -955,11 +966,14 @@ class CronArchive
private function initTokenAuth()
{
- $superUser = Db::get()->fetchRow("SELECT login, token_auth
- FROM " . Common::prefixTable("user") . "
- WHERE superuser_access = 1
- ORDER BY date_registered ASC");
- $this->token_auth = $superUser['token_auth'];
+ $token = '';
+
+ /**
+ * @ignore
+ */
+ Piwik::postEvent('CronArchive.getTokenAuth', array(&$token));
+
+ $this->token_auth = $token;
}
private function initPiwikHost($piwikUrl = false)
@@ -978,12 +992,12 @@ class CronArchive
$this->logFatalErrorUrlExpected();
}
- if(!\Piwik\UrlHelper::isLookLikeUrl($piwikUrl)) {
+ if (!\Piwik\UrlHelper::isLookLikeUrl($piwikUrl)) {
// try adding http:// in case it's missing
$piwikUrl = "http://" . $piwikUrl;
}
- if(!\Piwik\UrlHelper::isLookLikeUrl($piwikUrl)) {
+ if (!\Piwik\UrlHelper::isLookLikeUrl($piwikUrl)) {
$this->logFatalErrorUrlExpected();
}
@@ -1099,12 +1113,14 @@ class CronArchive
$websiteDayHasFinishedSinceLastRun = APISitesManager::getInstance()->getSitesIdFromTimezones($timezones);
$websiteDayHasFinishedSinceLastRun = array_diff($websiteDayHasFinishedSinceLastRun, $websiteIds);
$this->websiteDayHasFinishedSinceLastRun = $websiteDayHasFinishedSinceLastRun;
+
if (count($websiteDayHasFinishedSinceLastRun) > 0) {
$ids = !empty($websiteDayHasFinishedSinceLastRun) ? ", IDs: " . implode(", ", $websiteDayHasFinishedSinceLastRun) : "";
$this->log("- Will process " . count($websiteDayHasFinishedSinceLastRun)
. " other websites because the last time they were archived was on a different day (in the website's timezone) "
. $ids);
}
+
return $websiteDayHasFinishedSinceLastRun;
}
@@ -1170,6 +1186,7 @@ class CronArchive
$this->log("WARNING: Automatically increasing --force-timeout-for-periods from {$this->forceTimeoutPeriod} to "
. $this->todayArchiveTimeToLive
. " to match the cache timeout for Today's report specified in Piwik UI > Settings > General Settings");
+
return $this->todayArchiveTimeToLive;
}
@@ -1178,11 +1195,13 @@ class CronArchive
if (empty($this->shouldArchiveAllPeriodsSince)) {
return false;
}
+
if (is_numeric($this->shouldArchiveAllPeriodsSince)
&& $this->shouldArchiveAllPeriodsSince > 1
) {
return (int)$this->shouldArchiveAllPeriodsSince;
}
+
return true;
}
@@ -1192,6 +1211,7 @@ class CronArchive
protected function removeWebsiteFromInvalidatedWebsites($idSite)
{
$websiteIdsInvalidated = APICoreAdminHome::getWebsiteIdsToInvalidate();
+
if (count($websiteIdsInvalidated)) {
$found = array_search($idSite, $websiteIdsInvalidated);
if ($found !== false) {
@@ -1209,25 +1229,29 @@ class CronArchive
private function getVisitsLastPeriodFromApiResponse($stats)
{
- if(empty($stats)) {
+ if (empty($stats)) {
return 0;
}
+
$today = end($stats);
+
return $today['nb_visits'];
}
private function getVisitsFromApiResponse($stats)
{
- if(empty($stats)) {
+ if (empty($stats)) {
return 0;
}
+
$visits = 0;
foreach($stats as $metrics) {
- if(empty($metrics['nb_visits'])) {
+ if (empty($metrics['nb_visits'])) {
continue;
}
$visits += $metrics['nb_visits'];
}
+
return $visits;
}
@@ -1240,9 +1264,11 @@ class CronArchive
private function getApiDateParameter($idSite, $period, $lastTimestampWebsiteProcessed = false)
{
$dateRangeForced = $this->getDateRangeToProcess();
- if(!empty($dateRangeForced)) {
+
+ if (!empty($dateRangeForced)) {
return $dateRangeForced;
}
+
return $this->getDateLastN($idSite, $period, $lastTimestampWebsiteProcessed);
}
@@ -1256,7 +1282,7 @@ class CronArchive
*/
private function logArchivedWebsite($idSite, $period, $date, $visitsInLastPeriods, $visitsToday, Timer $timer)
{
- if(substr($date, 0, 4) === 'last') {
+ if (substr($date, 0, 4) === 'last') {
$visitsInLastPeriods = (int)$visitsInLastPeriods . " visits in last " . $date . " " . $period . "s, ";
$thisPeriod = $period == "day" ? "today" : "this " . $period;
$visitsInLastPeriod = (int)$visitsToday . " visits " . $thisPeriod . ", ";
@@ -1276,9 +1302,11 @@ class CronArchive
if (empty($this->restrictToDateRange)) {
return false;
}
+
if (strpos($this->restrictToDateRange, ',') === false) {
throw new Exception("--force-date-range expects a date range ie. YYYY-MM-DD,YYYY-MM-DD");
}
+
return $this->restrictToDateRange;
}
@@ -1289,6 +1317,7 @@ class CronArchive
{
$this->restrictToPeriods = array_intersect($this->restrictToPeriods, $this->getDefaultPeriodsToProcess());
$this->restrictToPeriods = array_intersect($this->restrictToPeriods, PeriodFactory::getPeriodsEnabledForAPI());
+
return $this->restrictToPeriods;
}
@@ -1311,9 +1340,10 @@ class CronArchive
private function shouldProcessPeriod($period)
{
- if(empty($this->shouldArchiveOnlySpecificPeriods)) {
+ if (empty($this->shouldArchiveOnlySpecificPeriods)) {
return true;
}
+
return in_array($period, $this->shouldArchiveOnlySpecificPeriods);
}
@@ -1344,6 +1374,7 @@ class CronArchive
if (!empty($this->dateLastForced)) {
$dateLast = $this->dateLastForced;
}
+
return "last" . $dateLast;
}
@@ -1352,9 +1383,10 @@ class CronArchive
*/
private function getConcurrentRequestsPerWebsite()
{
- if ($this->concurrentRequestsPerWebsite !== false) {
+ if (false !== $this->concurrentRequestsPerWebsite) {
return $this->concurrentRequestsPerWebsite;
}
+
return self::MAX_CONCURRENT_API_REQUESTS;
}
}
diff --git a/core/DataAccess/ArchivePurger.php b/core/DataAccess/ArchivePurger.php
index 94e1622376..d795ee6fba 100644
--- a/core/DataAccess/ArchivePurger.php
+++ b/core/DataAccess/ArchivePurger.php
@@ -31,16 +31,7 @@ class ArchivePurger
* Select the archives that have already been invalidated and have been since re-processed.
* It purges records for each distinct { archive name (includes segment hash) , idsite, date, period } tuple.
*/
- $query = '
- SELECT t1.idarchive FROM `' . $archiveTable . '` t1
- INNER JOIN `' . $archiveTable . '` t2
- ON t1.name = t2.name AND t1.idsite=t2.idsite
- AND t1.date1=t2.date1 AND t1.date2=t2.date2 AND t1.period=t2.period
- WHERE t1.value = ' . ArchiveWriter::DONE_INVALIDATED . '
- AND t2.value IN(' . ArchiveWriter::DONE_OK . ', ' . ArchiveWriter::DONE_OK_TEMPORARY . ')
- AND t1.ts_archived < t2.ts_archived AND t1.name LIKE \'done%\'';
-
- $result = Db::fetchAll($query);
+ $result = self::getModel()->purgeInvalidatedArchiveTable($archiveTable);
if (count($result) > 0) {
$archiveIds = array_map(
@@ -59,36 +50,39 @@ class ArchivePurger
}
}
+ private static function getModel()
+ {
+ return new Model();
+ }
public static function purgeOutdatedArchives(Date $dateStart)
{
$purgeArchivesOlderThan = Rules::shouldPurgeOutdatedArchives($dateStart);
+
if (!$purgeArchivesOlderThan) {
return;
}
$idArchivesToDelete = self::getTemporaryArchiveIdsOlderThan($dateStart, $purgeArchivesOlderThan);
+
if (!empty($idArchivesToDelete)) {
self::deleteArchiveIds($dateStart, $idArchivesToDelete);
}
+
self::deleteArchivesWithPeriodRange($dateStart);
Log::debug("Purging temporary archives: done [ purged archives older than %s in %s ] [Deleted IDs: %s]",
- $purgeArchivesOlderThan,
- $dateStart->toString("Y-m"),
- implode(',', $idArchivesToDelete));
+ $purgeArchivesOlderThan,
+ $dateStart->toString("Y-m"),
+ implode(',', $idArchivesToDelete));
}
protected static function getTemporaryArchiveIdsOlderThan(Date $date, $purgeArchivesOlderThan)
{
- $query = "SELECT idarchive
- FROM " . ArchiveTableCreator::getNumericTable($date) . "
- WHERE name LIKE 'done%'
- AND (( value = " . ArchiveWriter::DONE_OK_TEMPORARY . "
- AND ts_archived < ?)
- OR value = " . ArchiveWriter::DONE_ERROR . ")";
-
- $result = Db::fetchAll($query, array($purgeArchivesOlderThan));
+ $archiveTable = ArchiveTableCreator::getNumericTable($date);
+
+ $result = self::getModel()->getTemporaryArchivesOlderThan($archiveTable, $purgeArchivesOlderThan);
+
$idArchivesToDelete = array();
if (!empty($result)) {
foreach ($result as $row) {
@@ -104,36 +98,25 @@ class ArchivePurger
*/
protected static function deleteArchivesWithPeriodRange(Date $date)
{
- $query = "DELETE FROM %s WHERE period = ? AND ts_archived < ?";
-
- $yesterday = Date::factory('yesterday')->getDateTime();
- $bind = array(Piwik::$idPeriods['range'], $yesterday);
$numericTable = ArchiveTableCreator::getNumericTable($date);
- Db::query(sprintf($query, $numericTable), $bind);
+ $blobTable = ArchiveTableCreator::getBlobTable($date);
+ $yesterday = Date::factory('yesterday')->getDateTime();
+
Log::debug("Purging Custom Range archives: done [ purged archives older than %s from %s / blob ]",
- $yesterday,
- $numericTable);
- try {
- Db::query(sprintf($query, ArchiveTableCreator::getBlobTable($date)), $bind);
- } catch (Exception $e) {
- // Individual blob tables could be missing
- }
+ $yesterday, $numericTable);
+
+ self::getModel()->deleteArchivesWithPeriodRange($numericTable, $blobTable, Piwik::$idPeriods['range'], $yesterday);
}
protected static function deleteArchiveIds(Date $date, $idArchivesToDelete)
{
- $batches = array_chunk($idArchivesToDelete, 1000);
- foreach ($batches as $idsToDelete) {
- $query = "DELETE FROM %s WHERE idarchive IN (" . implode(',', $idsToDelete) . ")";
+ $batches = array_chunk($idArchivesToDelete, 1000);
+ $numericTable = ArchiveTableCreator::getNumericTable($date);
+ $blobTable = ArchiveTableCreator::getBlobTable($date);
- Db::query(sprintf($query, ArchiveTableCreator::getNumericTable($date)));
- try {
- Db::query(sprintf($query, ArchiveTableCreator::getBlobTable($date)));
- } catch (Exception $e) {
- // Individual blob tables could be missing
- }
+ foreach ($batches as $idsToDelete) {
+ self::getModel()->deleteArchiveIds($numericTable, $blobTable, $idsToDelete);
}
-
}
}
diff --git a/core/DataAccess/ArchiveSelector.php b/core/DataAccess/ArchiveSelector.php
index 5a715e6939..a342257eca 100644
--- a/core/DataAccess/ArchiveSelector.php
+++ b/core/DataAccess/ArchiveSelector.php
@@ -14,10 +14,8 @@ use Piwik\ArchiveProcessor;
use Piwik\Common;
use Piwik\Date;
use Piwik\Db;
-use Piwik\Log;
use Piwik\Period;
use Piwik\Period\Range;
-use Piwik\Piwik;
use Piwik\Segment;
/**
@@ -40,40 +38,36 @@ class ArchiveSelector
const NB_VISITS_CONVERTED_RECORD_LOOKED_UP = "nb_visits_converted";
+ private static function getModel()
+ {
+ return new Model();
+ }
+
public static function getArchiveIdAndVisits(ArchiveProcessor\Parameters $params, $minDatetimeArchiveProcessedUTC)
{
- $dateStart = $params->getPeriod()->getDateStart();
- $bindSQL = array($params->getSite()->getId(),
- $dateStart->toString('Y-m-d'),
- $params->getPeriod()->getDateEnd()->toString('Y-m-d'),
- $params->getPeriod()->getId(),
- );
-
- $timeStampWhere = '';
+ $idSite = $params->getSite()->getId();
+ $period = $params->getPeriod()->getId();
+ $dateStart = $params->getPeriod()->getDateStart();
+ $dateStartIso = $dateStart->toString('Y-m-d');
+ $dateEndIso = $params->getPeriod()->getDateEnd()->toString('Y-m-d');
+
+ $numericTable = ArchiveTableCreator::getNumericTable($dateStart);
+
+ $minDatetimeIsoArchiveProcessedUTC = null;
if ($minDatetimeArchiveProcessedUTC) {
- $timeStampWhere = " AND ts_archived >= ? ";
- $bindSQL[] = Date::factory($minDatetimeArchiveProcessedUTC)->getDatetime();
+ $minDatetimeIsoArchiveProcessedUTC = Date::factory($minDatetimeArchiveProcessedUTC)->getDatetime();
}
$requestedPlugin = $params->getRequestedPlugin();
- $segment = $params->getSegment();
+ $segment = $params->getSegment();
$isSkipAggregationOfSubTables = $params->isSkipAggregationOfSubTables();
-
$plugins = array("VisitsSummary", $requestedPlugin);
- $sqlWhereArchiveName = self::getNameCondition($plugins, $segment, $isSkipAggregationOfSubTables);
-
- $sqlQuery = " SELECT idarchive, value, name, date1 as startDate
- FROM " . ArchiveTableCreator::getNumericTable($dateStart) . "``
- WHERE idsite = ?
- AND date1 = ?
- AND date2 = ?
- AND period = ?
- AND ( ($sqlWhereArchiveName)
- OR name = '" . self::NB_VISITS_RECORD_LOOKED_UP . "'
- OR name = '" . self::NB_VISITS_CONVERTED_RECORD_LOOKED_UP . "')
- $timeStampWhere
- ORDER BY idarchive DESC";
- $results = Db::fetchAll($sqlQuery, $bindSQL);
+
+ $doneFlags = self::getDoneFlags($plugins, $segment, $isSkipAggregationOfSubTables);
+ $possibleValues = self::getPossibleValues();
+
+ $results = self::getModel()->getArchiveIdAndVisits($numericTable, $idSite, $period, $dateStartIso, $dateEndIso, $minDatetimeIsoArchiveProcessedUTC, $doneFlags, $possibleValues);
+
if (empty($results)) {
return false;
}
@@ -83,9 +77,8 @@ class ArchiveSelector
list($visits, $visitsConverted) = self::getVisitsMetricsFromResults($idArchive, $idArchiveVisitsSummary, $results);
- if ($visits === false
- && $idArchive === false
- ) {
+ if (false === $visits && false === $idArchive) {
+
return false;
}
@@ -96,9 +89,11 @@ class ArchiveSelector
{
$visits = $visitsConverted = false;
$archiveWithVisitsMetricsWasFound = ($idArchiveVisitsSummary !== false);
+
if ($archiveWithVisitsMetricsWasFound) {
$visits = $visitsConverted = 0;
}
+
foreach ($results as $result) {
if (in_array($result['idarchive'], array($idArchive, $idArchiveVisitsSummary))) {
$value = (int)$result['value'];
@@ -114,6 +109,7 @@ class ArchiveSelector
}
}
}
+
return array($visits, $visitsConverted);
}
@@ -121,6 +117,7 @@ class ArchiveSelector
{
$idArchive = false;
$namesRequestedPlugin = Rules::getDoneFlags(array($requestedPlugin), $segment, $isSkipAggregationOfSubTables);
+
foreach ($results as $result) {
if ($idArchive === false
&& in_array($result['name'], $namesRequestedPlugin)
@@ -129,6 +126,7 @@ class ArchiveSelector
break;
}
}
+
return $idArchive;
}
@@ -150,7 +148,7 @@ class ArchiveSelector
*/
public static function getArchiveIds($siteIds, $periods, $segment, $plugins, $isSkipAggregationOfSubTables = false)
{
- if(empty($siteIds)) {
+ if (empty($siteIds)) {
throw new \Exception("Website IDs could not be read from the request, ie. idSite=");
}
@@ -200,8 +198,10 @@ class ArchiveSelector
$sql = sprintf($getArchiveIdsSql, $table, $dateCondition);
+ $archiveIds = Db::fetchAll($sql, $bind);
+
// get the archive IDs
- foreach (Db::fetchAll($sql, $bind) as $row) {
+ foreach ($archiveIds as $row) {
$archiveName = $row['name'];
//FIXMEA duplicate with Archive.php
@@ -251,18 +251,23 @@ class ArchiveSelector
// get data from every table we're querying
$rows = array();
foreach ($archiveIds as $period => $ids) {
+
if (empty($ids)) {
throw new Exception("Unexpected: id archive not found for period '$period' '");
}
+
// $period = "2009-01-04,2009-01-04",
$date = Date::factory(substr($period, 0, 10));
+
if ($archiveDataType == 'numeric') {
$table = ArchiveTableCreator::getNumericTable($date);
} else {
$table = ArchiveTableCreator::getBlobTable($date);
}
- $sql = sprintf($getValuesSql, $table, implode(',', $ids));
+
+ $sql = sprintf($getValuesSql, $table, implode(',', $ids));
$dataRows = Db::fetchAll($sql, $bind);
+
foreach ($dataRows as $row) {
$rows[] = $row;
}
@@ -284,10 +289,41 @@ class ArchiveSelector
{
// the flags used to tell how the archiving process for a specific archive was completed,
// if it was completed
+ $doneFlags = self::getDoneFlags($plugins, $segment, $isSkipAggregationOfSubTables);
+ $allDoneFlags = "'" . implode("','", $doneFlags) . "'";
+
+ $possibleValues = self::getPossibleValues();
+
+ // create the SQL to find archives that are DONE
+ return "((name IN ($allDoneFlags)) AND (value IN (" . implode(',', $possibleValues) . ")))";
+ }
+
+ /**
+ * Returns the SQL condition used to find successfully completed archives that
+ * this instance is querying for.
+ *
+ * @param array $plugins
+ * @param Segment $segment
+ * @param bool $isSkipAggregationOfSubTables
+ * @return string
+ */
+ private static function getDoneFlags(array $plugins, Segment $segment, $isSkipAggregationOfSubTables)
+ {
+ // the flags used to tell how the archiving process for a specific archive was completed,
+ // if it was completed
$doneFlags = Rules::getDoneFlags($plugins, $segment, $isSkipAggregationOfSubTables);
- $allDoneFlags = "'" . implode("','", $doneFlags) . "'";
+ return $doneFlags;
+ }
+ /**
+ * Returns the SQL condition used to find successfully completed archives that
+ * this instance is querying for.
+ *
+ * @return string
+ */
+ private static function getPossibleValues()
+ {
$possibleValues = array(ArchiveWriter::DONE_OK, ArchiveWriter::DONE_OK_TEMPORARY);
if (!Rules::isRequestAuthorizedToArchive()) {
@@ -295,9 +331,7 @@ class ArchiveSelector
$possibleValues[] = ArchiveWriter::DONE_INVALIDATED;
}
- // create the SQL to find archives that are DONE
- return "((name IN ($allDoneFlags)) AND " .
- " (value IN (" . implode(',', $possibleValues) . ")))";
+ return $possibleValues;
}
}
diff --git a/core/DataAccess/ArchiveTableCreator.php b/core/DataAccess/ArchiveTableCreator.php
index 67abd2b123..230c7da05a 100644
--- a/core/DataAccess/ArchiveTableCreator.php
+++ b/core/DataAccess/ArchiveTableCreator.php
@@ -9,7 +9,6 @@
namespace Piwik\DataAccess;
-use Exception;
use Piwik\Common;
use Piwik\Date;
use Piwik\Db;
@@ -18,8 +17,7 @@ use Piwik\DbHelper;
class ArchiveTableCreator
{
const NUMERIC_TABLE = "numeric";
-
- const BLOB_TABLE = "blob";
+ const BLOB_TABLE = "blob";
public static $tablesAlreadyInstalled = null;
@@ -38,7 +36,9 @@ class ArchiveTableCreator
$tableNamePrefix = "archive_" . $type;
$tableName = $tableNamePrefix . "_" . $date->toString('Y_m');
$tableName = Common::prefixTable($tableName);
+
self::createArchiveTablesIfAbsent($tableName, $tableNamePrefix);
+
return $tableName;
}
@@ -49,24 +49,16 @@ class ArchiveTableCreator
}
if (!in_array($tableName, self::$tablesAlreadyInstalled)) {
- $db = Db::get();
- $sql = DbHelper::getTableCreateSql($tableNamePrefix);
-
- // replace table name template by real name
- $tableNamePrefix = Common::prefixTable($tableNamePrefix);
- $sql = str_replace($tableNamePrefix, $tableName, $sql);
- try {
- $db->query($sql);
- } catch (Exception $e) {
- // accept mysql error 1050: table already exists, throw otherwise
- if (!$db->isErrNo($e, '1050')) {
- throw $e;
- }
- }
+ self::getModel()->createArchiveTable($tableName, $tableNamePrefix);
self::$tablesAlreadyInstalled[] = $tableName;
}
}
+ private static function getModel()
+ {
+ return new Model();
+ }
+
public static function clear()
{
self::$tablesAlreadyInstalled = null;
@@ -89,6 +81,7 @@ class ArchiveTableCreator
}
$archiveTables = array();
+
foreach (self::$tablesAlreadyInstalled as $table) {
if (strpos($table, 'archive_numeric_') !== false
|| strpos($table, 'archive_blob_') !== false
@@ -96,13 +89,15 @@ class ArchiveTableCreator
$archiveTables[] = $table;
}
}
+
return $archiveTables;
}
public static function getDateFromTableName($tableName)
{
$tableName = Common::unprefixTable($tableName);
- $date = str_replace(array('archive_numeric_', 'archive_blob_'), '', $tableName);
+ $date = str_replace(array('archive_numeric_', 'archive_blob_'), '', $tableName);
+
return $date;
}
@@ -111,9 +106,11 @@ class ArchiveTableCreator
if (strpos($tableName, 'archive_numeric_') !== false) {
return self::NUMERIC_TABLE;
}
+
if (strpos($tableName, 'archive_blob_') !== false) {
return self::BLOB_TABLE;
}
+
return false;
}
}
diff --git a/core/DataAccess/ArchiveWriter.php b/core/DataAccess/ArchiveWriter.php
index 70f2159b67..1ea6b0d08c 100644
--- a/core/DataAccess/ArchiveWriter.php
+++ b/core/DataAccess/ArchiveWriter.php
@@ -23,7 +23,6 @@ use Piwik\Period;
*/
class ArchiveWriter
{
- const PREFIX_SQL_LOCK = "locked_";
/**
* Flag stored at the end of the archiving
*
@@ -64,9 +63,10 @@ class ArchiveWriter
public function __construct(ArchiveProcessor\Parameters $params, $isArchiveTemporary)
{
$this->idArchive = false;
- $this->idSite = $params->getSite()->getId();
- $this->segment = $params->getSegment();
- $this->period = $params->getPeriod();
+ $this->idSite = $params->getSite()->getId();
+ $this->segment = $params->getSegment();
+ $this->period = $params->getPeriod();
+
$idSites = array($this->idSite);
$this->doneFlag = Rules::getDoneStringFlagFor($idSites, $this->segment, $this->period->getLabel(), $params->getRequestedPlugin(), $params->isSkipAggregationOfSubTables());
$this->isArchiveTemporary = $isArchiveTemporary;
@@ -92,7 +92,7 @@ class ArchiveWriter
$newName = $name . '_' . $id;
}
- $value = $this->compress($value);
+ $value = $this->compress($value);
$clean[] = array($newName, $value);
}
$this->insertBulkRecords($clean);
@@ -108,6 +108,7 @@ class ArchiveWriter
if ($this->idArchive === false) {
throw new Exception("Must call allocateNewArchiveId() first");
}
+
return $this->idArchive;
}
@@ -119,7 +120,11 @@ class ArchiveWriter
public function finalizeArchive()
{
- $this->deletePreviousArchiveStatus();
+ $numericTable = $this->getTableNumeric();
+ $idArchive = $this->getIdArchive();
+
+ $this->getModel()->deletePreviousArchiveStatus($numericTable, $idArchive, $this->doneFlag);
+
$this->logArchiveStatusAsFinal();
}
@@ -128,28 +133,8 @@ class ArchiveWriter
if (Db::get()->hasBlobDataType()) {
return gzcompress($data);
}
- return $data;
- }
-
- protected function getArchiveLockName()
- {
- $numericTable = $this->getTableNumeric();
- $dbLockName = "allocateNewArchiveId.$numericTable";
- return $dbLockName;
- }
-
- protected function acquireArchiveTableLock()
- {
- $dbLockName = $this->getArchiveLockName();
- if (Db::getDbLock($dbLockName, $maxRetries = 30) === false) {
- throw new Exception("allocateNewArchiveId: Cannot get named lock $dbLockName.");
- }
- }
- protected function releaseArchiveTableLock()
- {
- $dbLockName = $this->getArchiveLockName();
- Db::releaseDbLock($dbLockName);
+ return $data;
}
protected function allocateNewArchiveId()
@@ -171,56 +156,31 @@ class ArchiveWriter
{
$numericTable = $this->getTableNumeric();
$idSite = $this->idSite;
+ $date = date("Y-m-d H:i:s");
- $this->acquireArchiveTableLock();
+ $id = $this->getModel()->insertNewArchiveId($numericTable, $idSite, $date);
- $locked = self::PREFIX_SQL_LOCK . Common::generateUniqId();
- $date = date("Y-m-d H:i:s");
- $insertSql = "INSERT INTO $numericTable "
- . " SELECT IFNULL( MAX(idarchive), 0 ) + 1,
- '" . $locked . "',
- " . (int)$idSite . ",
- '" . $date . "',
- '" . $date . "',
- 0,
- '" . $date . "',
- 0 "
- . " FROM $numericTable as tb1";
- Db::get()->exec($insertSql);
-
- $this->releaseArchiveTableLock();
-
- $selectIdSql = "SELECT idarchive FROM $numericTable WHERE name = ? LIMIT 1";
- $id = Db::get()->fetchOne($selectIdSql, $locked);
return $id;
}
- protected function logArchiveStatusAsIncomplete()
+ private function getModel()
{
- $statusWhileProcessing = self::DONE_ERROR;
- $this->insertRecord($this->doneFlag, $statusWhileProcessing);
+ return new Model();
}
- protected function deletePreviousArchiveStatus()
+ protected function logArchiveStatusAsIncomplete()
{
- // without advisory lock here, the DELETE would acquire Exclusive Lock
- $this->acquireArchiveTableLock();
-
- Db::query("DELETE FROM " . $this->getTableNumeric() . "
- WHERE idarchive = ? AND (name = '" . $this->doneFlag
- . "' OR name LIKE '" . self::PREFIX_SQL_LOCK . "%')",
- array($this->getIdArchive())
- );
-
- $this->releaseArchiveTableLock();
+ $this->insertRecord($this->doneFlag, self::DONE_ERROR);
}
protected function logArchiveStatusAsFinal()
{
$status = self::DONE_OK;
+
if ($this->isArchiveTemporary) {
$status = self::DONE_OK_TEMPORARY;
}
+
$this->insertRecord($this->doneFlag, $status);
}
@@ -233,27 +193,37 @@ class ArchiveWriter
foreach ($records as $record) {
$this->insertRecord($record[0], $record[1]);
}
+
return true;
}
+
$bindSql = $this->getInsertRecordBind();
- $values = array();
+ $values = array();
$valueSeen = false;
foreach ($records as $record) {
// don't record zero
- if (empty($record[1])) continue;
+ if (empty($record[1])) {
+ continue;
+ }
- $bind = $bindSql;
- $bind[] = $record[0]; // name
- $bind[] = $record[1]; // value
+ $bind = $bindSql;
+ $bind[] = $record[0]; // name
+ $bind[] = $record[1]; // value
$values[] = $bind;
$valueSeen = $record[1];
}
- if (empty($values)) return true;
+
+ if (empty($values)) {
+ return true;
+ }
$tableName = $this->getTableNameToInsert($valueSeen);
- BatchInsert::tableInsertBatch($tableName, $this->getInsertFields(), $values);
+ $fields = $this->getInsertFields();
+
+ BatchInsert::tableInsertBatch($tableName, $fields, $values);
+
return true;
}
@@ -272,15 +242,11 @@ class ArchiveWriter
}
$tableName = $this->getTableNameToInsert($value);
+ $fields = $this->getInsertFields();
+ $record = $this->getInsertRecordBind();
+
+ $this->getModel()->insertRecord($tableName, $fields, $record, $name, $value);
- // duplicate idarchives are Ignored, see https://github.com/piwik/piwik/issues/987
- $query = "INSERT IGNORE INTO " . $tableName . "
- (" . implode(", ", $this->getInsertFields()) . ")
- VALUES (?,?,?,?,?,?,?,?)";
- $bindSql = $this->getInsertRecordBind();
- $bindSql[] = $name;
- $bindSql[] = $value;
- Db::query($query, $bindSql);
return true;
}
@@ -299,6 +265,7 @@ class ArchiveWriter
if (is_numeric($value)) {
return $this->getTableNumeric();
}
+
return ArchiveTableCreator::getBlobTable($this->dateStart);
}
diff --git a/core/DataAccess/LogAggregator.php b/core/DataAccess/LogAggregator.php
index 95a7603176..83946b1f6f 100644
--- a/core/DataAccess/LogAggregator.php
+++ b/core/DataAccess/LogAggregator.php
@@ -292,52 +292,61 @@ class LogAggregator
$tableName = self::LOG_VISIT_TABLE;
$availableMetrics = $this->getVisitsMetricFields();
- $select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics, $metrics);
- $from = array($tableName);
- $where = $this->getWhereStatement($tableName, self::VISIT_DATETIME_FIELD, $where);
+ $select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics, $metrics);
+ $from = array($tableName);
+ $where = $this->getWhereStatement($tableName, self::VISIT_DATETIME_FIELD, $where);
$groupBy = $this->getGroupByStatement($dimensions, $tableName);
$orderBy = false;
if ($rankingQuery) {
$orderBy = '`' . Metrics::INDEX_NB_VISITS . '` DESC';
}
+
$query = $this->generateQuery($select, $from, $where, $groupBy, $orderBy);
if ($rankingQuery) {
unset($availableMetrics[Metrics::INDEX_MAX_ACTIONS]);
$sumColumns = array_keys($availableMetrics);
+
if ($metrics) {
$sumColumns = array_intersect($sumColumns, $metrics);
}
+
$rankingQuery->addColumn($sumColumns, 'sum');
if ($this->isMetricRequested(Metrics::INDEX_MAX_ACTIONS, $metrics)) {
$rankingQuery->addColumn(Metrics::INDEX_MAX_ACTIONS, 'max');
}
+
return $rankingQuery->execute($query['sql'], $query['bind']);
}
+
return $this->getDb()->query($query['sql'], $query['bind']);
}
protected function getSelectsMetrics($metricsAvailable, $metricsRequested = false)
{
$selects = array();
+
foreach ($metricsAvailable as $metricId => $statement) {
if ($this->isMetricRequested($metricId, $metricsRequested)) {
- $aliasAs = $this->getSelectAliasAs($metricId);
+ $aliasAs = $this->getSelectAliasAs($metricId);
$selects[] = $statement . $aliasAs;
}
}
+
return $selects;
}
protected function getSelectStatement($dimensions, $tableName, $additionalSelects, array $availableMetrics, $requestedMetrics = false)
{
$dimensionsToSelect = $this->getDimensionsToSelect($dimensions, $additionalSelects);
+
$selects = array_merge(
$this->getSelectDimensions($dimensionsToSelect, $tableName),
$this->getSelectsMetrics($availableMetrics, $requestedMetrics),
!empty($additionalSelects) ? $additionalSelects : array()
);
+
$select = implode(self::FIELDS_SEPARATOR, $selects);
return $select;
}
@@ -354,6 +363,7 @@ class LogAggregator
if (empty($additionalSelects)) {
return $dimensions;
}
+
$dimensionsToSelect = array();
foreach ($dimensions as $selectAs => $dimension) {
$asAlias = $this->getSelectAliasAs($dimension);
@@ -363,6 +373,7 @@ class LogAggregator
}
}
}
+
$dimensionsToSelect = array_unique($dimensionsToSelect);
return $dimensionsToSelect;
}
@@ -381,6 +392,7 @@ class LogAggregator
{
foreach ($dimensions as $selectAs => &$field) {
$selectAsString = $field;
+
if (!is_numeric($selectAs)) {
$selectAsString = $selectAs;
} else {
@@ -389,16 +401,18 @@ class LogAggregator
$selectAsString = $appendSelectAs = false;
}
}
+
$isKnownField = !in_array($field, array('referrer_data'));
- if ($selectAsString == $field
- && $isKnownField
- ) {
+
+ if ($selectAsString == $field && $isKnownField) {
$field = $this->prefixColumn($field, $tableName);
}
+
if ($appendSelectAs && $selectAsString) {
$field = $this->prefixColumn($field, $tableName) . $this->getSelectAliasAs($selectAsString);
}
}
+
return $dimensions;
}
@@ -421,7 +435,7 @@ class LogAggregator
protected function isFieldFunctionOrComplexExpression($field)
{
return strpos($field, "(") !== false
- || strpos($field, "CASE") !== false;
+ || strpos($field, "CASE") !== false;
}
protected function getSelectAliasAs($metricId)
@@ -432,7 +446,7 @@ class LogAggregator
protected function isMetricRequested($metricId, $metricsRequested)
{
return $metricsRequested === false
- || in_array($metricId, $metricsRequested);
+ || in_array($metricId, $metricsRequested);
}
protected function getWhereStatement($tableName, $datetimeField, $extraWhere = false)
@@ -440,17 +454,20 @@ class LogAggregator
$where = "$tableName.$datetimeField >= ?
AND $tableName.$datetimeField <= ?
AND $tableName.idsite IN (". Common::getSqlStringFieldsArray($this->sites) . ")";
+
if (!empty($extraWhere)) {
$extraWhere = sprintf($extraWhere, $tableName, $tableName);
- $where .= ' AND ' . $extraWhere;
+ $where .= ' AND ' . $extraWhere;
}
+
return $where;
}
protected function getGroupByStatement($dimensions, $tableName)
{
$dimensions = $this->getSelectDimensions($dimensions, $tableName, $appendSelectAs = false);
- $groupBy = implode(", ", $dimensions);
+ $groupBy = implode(", ", $dimensions);
+
return $groupBy;
}
@@ -464,6 +481,7 @@ class LogAggregator
{
$bind = array($this->dateStart->getDateStartUTC(), $this->dateEnd->getDateEndUTC());
$bind = array_merge($bind, $this->sites);
+
return $bind;
}
@@ -494,7 +512,7 @@ class LogAggregator
*
* @param string $dimension One or more **log\_conversion\_item** columns to group aggregated data by.
* Eg, `'idaction_sku'` or `'idaction_sku, idaction_category'`.
- * @return Zend_Db_Statement A statement object that can be used to iterate through the query's
+ * @return \Zend_Db_Statement A statement object that can be used to iterate through the query's
* result set. See [above](#queryEcommerceItems-result-set) to learn more
* about what this query selects.
* @api
@@ -624,9 +642,9 @@ class LogAggregator
$tableName = self::LOG_ACTIONS_TABLE;
$availableMetrics = $this->getActionsMetricFields();
- $select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics, $metrics);
- $from = array($tableName);
- $where = $this->getWhereStatement($tableName, self::ACTION_DATETIME_FIELD, $where);
+ $select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics, $metrics);
+ $from = array($tableName);
+ $where = $this->getWhereStatement($tableName, self::ACTION_DATETIME_FIELD, $where);
$groupBy = $this->getGroupByStatement($dimensions, $tableName);
$orderBy = false;
@@ -638,12 +656,14 @@ class LogAggregator
foreach ($joinLogActionOnColumn as $i => $joinColumn) {
$tableAlias = 'log_action' . ($multiJoin ? $i + 1 : '');
+
if (strpos($joinColumn, ' ') === false) {
$joinOn = $tableAlias . '.idaction = ' . $tableName . '.' . $joinColumn;
} else {
- // more complex join column like IF(...)
+ // more complex join column like if (...)
$joinOn = $tableAlias . '.idaction = ' . $joinColumn;
}
+
$from[] = array(
'table' => 'log_action',
'tableAlias' => $tableAlias,
@@ -663,7 +683,9 @@ class LogAggregator
if ($metrics) {
$sumColumns = array_intersect($sumColumns, $metrics);
}
+
$rankingQuery->addColumn($sumColumns, 'sum');
+
return $rankingQuery->execute($query['sql'], $query['bind']);
}
@@ -733,21 +755,22 @@ class LogAggregator
* @param bool|string $where An optional SQL expression used in the SQL's **WHERE** clause.
* @param array $additionalSelects Additional SELECT fields that are not included in the group by
* clause. These can be aggregate expressions, eg, `SUM(somecol)`.
- * @return Zend_Db_Statement
+ * @return \Zend_Db_Statement
*/
public function queryConversionsByDimension($dimensions = array(), $where = false, $additionalSelects = array())
{
$dimensions = array_merge(array(self::IDGOAL_FIELD), $dimensions);
+ $tableName = self::LOG_CONVERSION_TABLE;
$availableMetrics = $this->getConversionsMetricFields();
- $tableName = self::LOG_CONVERSION_TABLE;
$select = $this->getSelectStatement($dimensions, $tableName, $additionalSelects, $availableMetrics);
- $from = array($tableName);
- $where = $this->getWhereStatement($tableName, self::CONVERSION_DATETIME_FIELD, $where);
+ $from = array($tableName);
+ $where = $this->getWhereStatement($tableName, self::CONVERSION_DATETIME_FIELD, $where);
$groupBy = $this->getGroupByStatement($dimensions, $tableName);
$orderBy = false;
- $query = $this->generateQuery($select, $from, $where, $groupBy, $orderBy);
+ $query = $this->generateQuery($select, $from, $where, $groupBy, $orderBy);
+
return $this->getDb()->query($query['sql'], $query['bind']);
}
@@ -824,14 +847,16 @@ class LogAggregator
{
$selects = array();
$extraCondition = '';
+
if ($restrictToReturningVisitors) {
// extra condition for the SQL SELECT that makes sure only returning visits are counted
// when creating the 'days since last visit' report
$extraCondition = 'and log_visit.visitor_returning = 1';
- $extraSelect = "sum(case when log_visit.visitor_returning = 0 then 1 else 0 end) "
- . " as `" . $selectColumnPrefix . 'General_NewVisits' . "`";
+ $extraSelect = "sum(case when log_visit.visitor_returning = 0 then 1 else 0 end) "
+ . " as `" . $selectColumnPrefix . 'General_NewVisits' . "`";
$selects[] = $extraSelect;
}
+
foreach ($ranges as $gap) {
if (count($gap) == 2) {
$lowerBound = $gap[0];
@@ -840,12 +865,11 @@ class LogAggregator
$selectAs = "$selectColumnPrefix$lowerBound-$upperBound";
$selects[] = "sum(case when $table.$column between $lowerBound and $upperBound $extraCondition" .
- " then 1 else 0 end) as `$selectAs`";
+ " then 1 else 0 end) as `$selectAs`";
} else {
$lowerBound = $gap[0];
- $selectAs = $selectColumnPrefix . ($lowerBound + 1) . urlencode('+');
-
+ $selectAs = $selectColumnPrefix . ($lowerBound + 1) . urlencode('+');
$selects[] = "sum(case when $table.$column > $lowerBound $extraCondition then 1 else 0 end) as `$selectAs`";
}
}
@@ -869,6 +893,7 @@ class LogAggregator
public static function makeArrayOneColumn($row, $columnName, $lookForThisPrefix = false)
{
$cleanRow = array();
+
foreach ($row as $label => $count) {
if (empty($lookForThisPrefix)
|| strpos($label, $lookForThisPrefix) === 0
@@ -877,6 +902,7 @@ class LogAggregator
$cleanRow[$cleanLabel] = array($columnName => $count);
}
}
+
return $cleanRow;
}
diff --git a/core/DataAccess/Model.php b/core/DataAccess/Model.php
new file mode 100644
index 0000000000..facaa5dd97
--- /dev/null
+++ b/core/DataAccess/Model.php
@@ -0,0 +1,229 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\DataAccess;
+
+use Exception;
+use Piwik\Common;
+use Piwik\Db;
+use Piwik\DbHelper;
+
+/**
+ * Cleans up outdated archives
+ *
+ * @package Piwik\DataAccess
+ */
+class Model
+{
+ const PREFIX_SQL_LOCK = "locked_";
+
+ public function purgeInvalidatedArchiveTable($archiveTable)
+ {
+ /**
+ * Select the archives that have already been invalidated and have been since re-processed.
+ * It purges records for each distinct { archive name (includes segment hash) , idsite, date, period } tuple.
+ */
+ $query = 'SELECT t1.idarchive FROM `' . $archiveTable . '` t1
+ INNER JOIN `' . $archiveTable . '` t2
+ ON t1.name = t2.name AND t1.idsite=t2.idsite
+ AND t1.date1=t2.date1 AND t1.date2=t2.date2 AND t1.period=t2.period
+ WHERE t1.value = ' . ArchiveWriter::DONE_INVALIDATED . '
+ AND t2.value IN(' . ArchiveWriter::DONE_OK . ', ' . ArchiveWriter::DONE_OK_TEMPORARY . ')
+ AND t1.ts_archived < t2.ts_archived AND t1.name LIKE \'done%\'';
+
+ $result = Db::fetchAll($query);
+
+ return $result;
+ }
+
+ public function getTemporaryArchivesOlderThan($archiveTable, $purgeArchivesOlderThan)
+ {
+ $query = "SELECT idarchive FROM " . $archiveTable . "
+ WHERE name LIKE 'done%'
+ AND (( value = " . ArchiveWriter::DONE_OK_TEMPORARY . "
+ AND ts_archived < ?)
+ OR value = " . ArchiveWriter::DONE_ERROR . ")";
+
+ return Db::fetchAll($query, array($purgeArchivesOlderThan));
+ }
+
+ /*
+ * Deleting "Custom Date Range" reports, since they can be re-processed and would take up un-necessary space
+ */
+ public function deleteArchivesWithPeriodRange($numericTable, $blobTable, $range, $date)
+ {
+ $query = "DELETE FROM %s WHERE period = ? AND ts_archived < ?";
+ $bind = array($range, $date);
+
+ Db::query(sprintf($query, $numericTable), $bind);
+
+ try {
+ Db::query(sprintf($query, $blobTable), $bind);
+ } catch (Exception $e) {
+ // Individual blob tables could be missing
+ }
+ }
+
+ public function deleteArchiveIds($numericTable, $blobTable, $idsToDelete)
+ {
+ $query = "DELETE FROM %s WHERE idarchive IN (" . implode(',', $idsToDelete) . ")";
+
+ Db::query(sprintf($query, $numericTable));
+
+ try {
+ Db::query(sprintf($query, $blobTable));
+ } catch (Exception $e) {
+ // Individual blob tables could be missing
+ }
+ }
+
+ public function getArchiveIdAndVisits($numericTable, $idSite, $period, $dateStartIso, $dateEndIso, $minDatetimeIsoArchiveProcessedUTC, $doneFlags, $possibleValues)
+ {
+ $bindSQL = array($idSite,
+ $dateStartIso,
+ $dateEndIso,
+ $period,
+ );
+
+ $timeStampWhere = '';
+ if ($minDatetimeIsoArchiveProcessedUTC) {
+ $timeStampWhere = " AND ts_archived >= ? ";
+ $bindSQL[] = $minDatetimeIsoArchiveProcessedUTC;
+ }
+
+ $sqlWhereArchiveName = self::getNameCondition($doneFlags, $possibleValues);
+
+ $sqlQuery = "SELECT idarchive, value, name, date1 as startDate FROM $numericTable
+ WHERE idsite = ?
+ AND date1 = ?
+ AND date2 = ?
+ AND period = ?
+ AND ( ($sqlWhereArchiveName)
+ OR name = '" . ArchiveSelector::NB_VISITS_RECORD_LOOKED_UP . "'
+ OR name = '" . ArchiveSelector::NB_VISITS_CONVERTED_RECORD_LOOKED_UP . "')
+ $timeStampWhere
+ ORDER BY idarchive DESC";
+ $results = Db::fetchAll($sqlQuery, $bindSQL);
+
+ return $results;
+ }
+
+ public function createArchiveTable($tableName, $tableNamePrefix)
+ {
+ $db = Db::get();
+ $sql = DbHelper::getTableCreateSql($tableNamePrefix);
+
+ // replace table name template by real name
+ $tableNamePrefix = Common::prefixTable($tableNamePrefix);
+ $sql = str_replace($tableNamePrefix, $tableName, $sql);
+
+ try {
+ $db->query($sql);
+ } catch (Exception $e) {
+ // accept mysql error 1050: table already exists, throw otherwise
+ if (!$db->isErrNo($e, '1050')) {
+ throw $e;
+ }
+ }
+ }
+
+ /**
+ * Locks the archive table to generate a new archive ID.
+ *
+ * We lock to make sure that
+ * if several archiving processes are running at the same time (for different websites and/or periods)
+ * then they will each use a unique archive ID.
+ *
+ * @return int
+ */
+ public function insertNewArchiveId($numericTable, $idSite, $date)
+ {
+ $this->acquireArchiveTableLock($numericTable);
+
+ $locked = self::PREFIX_SQL_LOCK . Common::generateUniqId();
+
+ $insertSql = "INSERT INTO $numericTable "
+ . " SELECT IFNULL( MAX(idarchive), 0 ) + 1,
+ '" . $locked . "',
+ " . (int)$idSite . ",
+ '" . $date . "',
+ '" . $date . "',
+ 0,
+ '" . $date . "',
+ 0 "
+ . " FROM $numericTable as tb1";
+ Db::get()->exec($insertSql);
+
+ $this->releaseArchiveTableLock($numericTable);
+
+ $selectIdSql = "SELECT idarchive FROM $numericTable WHERE name = ? LIMIT 1";
+ $id = Db::get()->fetchOne($selectIdSql, $locked);
+ return $id;
+ }
+
+ public function deletePreviousArchiveStatus($numericTable, $archiveId, $doneFlag)
+ {
+ // without advisory lock here, the DELETE would acquire Exclusive Lock
+ $this->acquireArchiveTableLock($numericTable);
+
+ Db::query("DELETE FROM $numericTable WHERE idarchive = ? AND (name = '" . $doneFlag
+ . "' OR name LIKE '" . self::PREFIX_SQL_LOCK . "%')",
+ array($archiveId)
+ );
+
+ $this->releaseArchiveTableLock($numericTable);
+ }
+
+ public function insertRecord($tableName, $fields, $record, $name, $value)
+ {
+ // duplicate idarchives are Ignored, see https://github.com/piwik/piwik/issues/987
+ $query = "INSERT IGNORE INTO " . $tableName . " (" . implode(", ", $fields) . ")
+ VALUES (?,?,?,?,?,?,?,?)";
+
+ $bindSql = $record;
+ $bindSql[] = $name;
+ $bindSql[] = $value;
+
+ Db::query($query, $bindSql);
+
+ return true;
+ }
+
+ /**
+ * Returns the SQL condition used to find successfully completed archives that
+ * this instance is querying for.
+ */
+ private static function getNameCondition($doneFlags, $possibleValues)
+ {
+ $allDoneFlags = "'" . implode("','", $doneFlags) . "'";
+
+ // create the SQL to find archives that are DONE
+ return "((name IN ($allDoneFlags)) AND (value IN (" . implode(',', $possibleValues) . ")))";
+ }
+
+ protected function acquireArchiveTableLock($numericTable)
+ {
+ $dbLockName = $this->getArchiveLockName($numericTable);
+
+ if (Db::getDbLock($dbLockName, $maxRetries = 30) === false) {
+ throw new Exception("allocateNewArchiveId: Cannot get named lock $dbLockName.");
+ }
+ }
+
+ protected function releaseArchiveTableLock($numericTable)
+ {
+ $dbLockName = $this->getArchiveLockName($numericTable);
+ Db::releaseDbLock($dbLockName);
+ }
+
+ protected function getArchiveLockName($numericTable)
+ {
+ return "allocateNewArchiveId.$numericTable";
+ }
+
+}
diff --git a/core/DataArray.php b/core/DataArray.php
index 042d5ae961..04c9897125 100644
--- a/core/DataArray.php
+++ b/core/DataArray.php
@@ -108,7 +108,7 @@ class DataArray
// In case the existing Row had no action metrics (eg. Custom Variable XYZ with "visit" scope)
// but the new Row has action metrics (eg. same Custom Variable XYZ this time with a "page" scope)
- if(!isset($oldRowToUpdate[Metrics::INDEX_MAX_ACTIONS])) {
+ if (!isset($oldRowToUpdate[Metrics::INDEX_MAX_ACTIONS])) {
$toZero = array(Metrics::INDEX_MAX_ACTIONS,
Metrics::INDEX_SUM_VISIT_LENGTH,
Metrics::INDEX_BOUNCE_COUNT,
@@ -247,8 +247,8 @@ class DataArray
$oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE] = round(max($newRowToAdd[Metrics::INDEX_EVENT_MAX_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE]), self::EVENT_VALUE_PRECISION);
// Update minimum only if it is set
- if($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] !== false) {
- if($oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] === false) {
+ if ($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] !== false) {
+ if ($oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] === false) {
$oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], self::EVENT_VALUE_PRECISION);
} else {
$oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round(min($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE]), self::EVENT_VALUE_PRECISION);
@@ -360,7 +360,7 @@ class DataArray
// if there are no "visit" column, we force one to prevent future complications
// eg. This helps the setDefaultColumnsToDisplay() call
- if(!isset($values[Metrics::INDEX_NB_VISITS])) {
+ if (!isset($values[Metrics::INDEX_NB_VISITS])) {
$values[Metrics::INDEX_NB_VISITS] = 0;
}
}
diff --git a/core/DataTable.php b/core/DataTable.php
index bc5a57cff6..4d308f7d21 100644
--- a/core/DataTable.php
+++ b/core/DataTable.php
@@ -469,11 +469,13 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
* metadata can be used to specify a different type of operation.
*
* @param \Piwik\DataTable $tableToSum
+ * @param bool $doAggregateSubTables
+ * @throws Exception
*/
public function addDataTable(DataTable $tableToSum, $doAggregateSubTables = true)
{
- if($tableToSum instanceof Simple) {
- if($tableToSum->getRowsCount() > 1) {
+ if ($tableToSum instanceof Simple) {
+ if ($tableToSum->getRowsCount() > 1) {
throw new Exception("Did not expect a Simple table with more than one row in addDataTable()");
}
$row = $tableToSum->getFirstRow();
@@ -897,7 +899,7 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
foreach ($this->getRows() as $row) {
$row->renameColumn($oldName, $newName);
- if($doRenameColumnsOfSubTables) {
+ if ($doRenameColumnsOfSubTables) {
if (($idSubDataTable = $row->getIdSubDataTable()) !== null) {
Manager::getInstance()->getTable($idSubDataTable)->renameColumn($oldName, $newName);
}
@@ -1602,7 +1604,7 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
} else {
$rowFound->sumRow($row, $copyMeta = true, $this->getMetadata(self::COLUMN_AGGREGATION_OPS_METADATA_NAME));
- if($doAggregateSubTables) {
+ if ($doAggregateSubTables) {
// if the row to add has a subtable whereas the current row doesn't
// we simply add it (cloning the subtable)
// if the row has the subtable already
diff --git a/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php b/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php
index 742f1aef6d..963ac9acbd 100644
--- a/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php
+++ b/core/DataTable/Filter/AddColumnsProcessedMetricsGoal.php
@@ -8,12 +8,10 @@
*/
namespace Piwik\DataTable\Filter;
-use Exception;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Piwik;
-use Piwik\Tracker\GoalManager;
/**
* Adds goal related metrics to a {@link DataTable} using metrics that already exist.
diff --git a/core/DataTable/Filter/BeautifyRangeLabels.php b/core/DataTable/Filter/BeautifyRangeLabels.php
index 534f484bff..bf5e8d65d9 100644
--- a/core/DataTable/Filter/BeautifyRangeLabels.php
+++ b/core/DataTable/Filter/BeautifyRangeLabels.php
@@ -65,7 +65,7 @@ class BeautifyRangeLabels extends ColumnCallbackReplace
parent::__construct($table, 'label', array($this, 'beautify'), array());
$this->labelSingular = $labelSingular;
- $this->labelPlural = $labelPlural;
+ $this->labelPlural = $labelPlural;
}
/**
diff --git a/core/DataTable/Filter/CalculateEvolutionFilter.php b/core/DataTable/Filter/CalculateEvolutionFilter.php
index 80f789919e..0ef6a30a2f 100755
--- a/core/DataTable/Filter/CalculateEvolutionFilter.php
+++ b/core/DataTable/Filter/CalculateEvolutionFilter.php
@@ -121,6 +121,7 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage
{
$value = self::getPercentageValue($value, $divisor, $this->quotientPrecision);
$value = self::appendPercentSign($value);
+
return $value;
}
@@ -152,6 +153,7 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage
if ($appendPercentSign) {
$number = self::appendPercentSign($number);
}
+
return $number;
}
@@ -165,6 +167,7 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage
if ($number > 0) {
$number = '+' . $number;
}
+
return $number;
}
diff --git a/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php b/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php
index 5cc83d8e3d..81d8eadf04 100644
--- a/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php
+++ b/core/DataTable/Filter/ColumnCallbackAddColumnQuotient.php
@@ -109,6 +109,7 @@ class ColumnCallbackAddColumnQuotient extends BaseFilter
if ($divisor > 0 && $value > 0) {
$quotient = round($value / $divisor, $this->quotientPrecision);
}
+
return $quotient;
}
diff --git a/core/DataTable/Filter/ColumnCallbackAddMetadata.php b/core/DataTable/Filter/ColumnCallbackAddMetadata.php
index e89000f8fd..c7945839cb 100644
--- a/core/DataTable/Filter/ColumnCallbackAddMetadata.php
+++ b/core/DataTable/Filter/ColumnCallbackAddMetadata.php
@@ -48,12 +48,12 @@ class ColumnCallbackAddMetadata extends BaseFilter
if (!is_array($columnsToRead)) {
$columnsToRead = array($columnsToRead);
}
- $this->columnsToRead = $columnsToRead;
- $this->functionToApply = $functionToApply;
+ $this->columnsToRead = $columnsToRead;
+ $this->functionToApply = $functionToApply;
$this->functionParameters = $functionParameters;
- $this->metadataToAdd = $metadataToAdd;
- $this->applyToSummaryRow = $applyToSummaryRow;
+ $this->metadataToAdd = $metadataToAdd;
+ $this->applyToSummaryRow = $applyToSummaryRow;
}
/**
diff --git a/core/DataTable/Filter/ColumnCallbackDeleteRow.php b/core/DataTable/Filter/ColumnCallbackDeleteRow.php
index 189fbd3b81..414bf71e8a 100644
--- a/core/DataTable/Filter/ColumnCallbackDeleteRow.php
+++ b/core/DataTable/Filter/ColumnCallbackDeleteRow.php
@@ -25,7 +25,6 @@ use Piwik\DataTable\BaseFilter;
*/
class ColumnCallbackDeleteRow extends BaseFilter
{
- private $columnToFilter;
private $function;
private $functionParams;
diff --git a/core/DataTable/Filter/ColumnCallbackReplace.php b/core/DataTable/Filter/ColumnCallbackReplace.php
index e33dfa9a14..ca53405d7e 100644
--- a/core/DataTable/Filter/ColumnCallbackReplace.php
+++ b/core/DataTable/Filter/ColumnCallbackReplace.php
@@ -54,14 +54,14 @@ class ColumnCallbackReplace extends BaseFilter
$extraColumnParameters = array())
{
parent::__construct($table);
- $this->functionToApply = $functionToApply;
+ $this->functionToApply = $functionToApply;
$this->functionParameters = $functionParameters;
if (!is_array($columnsToFilter)) {
$columnsToFilter = array($columnsToFilter);
}
- $this->columnsToFilter = $columnsToFilter;
+ $this->columnsToFilter = $columnsToFilter;
$this->extraColumnParameters = $extraColumnParameters;
}
@@ -73,6 +73,7 @@ class ColumnCallbackReplace extends BaseFilter
public function filter($table)
{
foreach ($table->getRows() as $row) {
+
$extraColumnParameters = array();
foreach ($this->extraColumnParameters as $columnName) {
$extraColumnParameters[] = $row->getColumn($columnName);
@@ -86,9 +87,11 @@ class ColumnCallbackReplace extends BaseFilter
}
$parameters = array_merge(array($value), $extraColumnParameters);
+
if (!is_null($this->functionParameters)) {
$parameters = array_merge($parameters, $this->functionParameters);
}
+
$newValue = call_user_func_array($this->functionToApply, $parameters);
$this->setElementToReplace($row, $column, $newValue);
$this->filterSubTable($row);
diff --git a/core/DataTable/Filter/ColumnDelete.php b/core/DataTable/Filter/ColumnDelete.php
index 5aac6d44a6..743f5afd86 100644
--- a/core/DataTable/Filter/ColumnDelete.php
+++ b/core/DataTable/Filter/ColumnDelete.php
@@ -91,6 +91,7 @@ class ColumnDelete extends BaseFilter
* See {@link ColumnDelete}.
*
* @param DataTable $table
+ * @return DataTable
*/
public function filter($table)
{
diff --git a/core/DataTable/Filter/GroupBy.php b/core/DataTable/Filter/GroupBy.php
index 8633fb2e9d..b899bb9059 100755
--- a/core/DataTable/Filter/GroupBy.php
+++ b/core/DataTable/Filter/GroupBy.php
@@ -59,9 +59,9 @@ class GroupBy extends BaseFilter
{
parent::__construct($table);
- $this->groupByColumn = $groupByColumn;
+ $this->groupByColumn = $groupByColumn;
$this->reduceFunction = $reduceFunction;
- $this->parameters = $parameters;
+ $this->parameters = $parameters;
}
/**
@@ -82,7 +82,7 @@ class GroupBy extends BaseFilter
// reduce the group by column of this row
$groupByColumnValue = $row->getColumn($this->groupByColumn);
- $parameters = array_merge(array($groupByColumnValue), $this->parameters);
+ $parameters = array_merge(array($groupByColumnValue), $this->parameters);
$groupByValue = call_user_func_array($this->reduceFunction, $parameters);
if (!isset($groupByRows[$groupByValue])) {
diff --git a/core/DataTable/Filter/Limit.php b/core/DataTable/Filter/Limit.php
index 5cf848a50e..415be29602 100644
--- a/core/DataTable/Filter/Limit.php
+++ b/core/DataTable/Filter/Limit.php
@@ -34,9 +34,9 @@ class Limit extends BaseFilter
public function __construct($table, $offset, $limit = -1, $keepSummaryRow = false)
{
parent::__construct($table);
- $this->offset = $offset;
- $this->limit = $limit;
+ $this->offset = $offset;
+ $this->limit = $limit;
$this->keepSummaryRow = $keepSummaryRow;
}
diff --git a/core/DataTable/Filter/Sort.php b/core/DataTable/Filter/Sort.php
index 683e93cffe..9df2250288 100644
--- a/core/DataTable/Filter/Sort.php
+++ b/core/DataTable/Filter/Sort.php
@@ -38,11 +38,13 @@ class Sort extends BaseFilter
public function __construct($table, $columnToSort, $order = 'desc', $naturalSort = true, $recursiveSort = false)
{
parent::__construct($table);
+
if ($recursiveSort) {
$table->enableRecursiveSort();
}
+
$this->columnToSort = $columnToSort;
- $this->naturalSort = $naturalSort;
+ $this->naturalSort = $naturalSort;
$this->setOrder($order);
}
@@ -55,10 +57,10 @@ class Sort extends BaseFilter
{
if ($order == 'asc') {
$this->order = 'asc';
- $this->sign = 1;
+ $this->sign = 1;
} else {
$this->order = 'desc';
- $this->sign = -1;
+ $this->sign = -1;
}
}
@@ -225,17 +227,21 @@ class Sort extends BaseFilter
if ($table instanceof Simple) {
return;
}
+
if (empty($this->columnToSort)) {
return;
}
+
$rows = $table->getRows();
if (count($rows) == 0) {
return;
}
+
$row = current($rows);
if ($row === false) {
return;
}
+
$this->columnToSort = $this->selectColumnToSort($row);
$value = $row->getColumn($this->columnToSort);
@@ -248,6 +254,7 @@ class Sort extends BaseFilter
$methodToUse = "sortString";
}
}
+
$table->sort(array($this, $methodToUse), $this->columnToSort);
}
}
diff --git a/core/DataTable/Filter/Truncate.php b/core/DataTable/Filter/Truncate.php
index 192d85b82f..120fc37760 100644
--- a/core/DataTable/Filter/Truncate.php
+++ b/core/DataTable/Filter/Truncate.php
@@ -89,9 +89,10 @@ class Truncate extends BaseFilter
return;
}
- $rows = $table->getRows();
- $count = $table->getRowsCount();
+ $rows = $table->getRows();
+ $count = $table->getRowsCount();
$newRow = new Row(array(Row::COLUMNS => array('label' => DataTable::LABEL_SUMMARY_ROW)));
+
for ($i = $this->truncateAfter; $i < $count; $i++) {
if (!isset($rows[$i])) {
// case when the last row is a summary row, it is not indexed by $cout but by DataTable::ID_SUMMARY_ROW
diff --git a/core/DataTable/Manager.php b/core/DataTable/Manager.php
index c19060b803..d225b8fb87 100644
--- a/core/DataTable/Manager.php
+++ b/core/DataTable/Manager.php
@@ -63,6 +63,7 @@ class Manager extends Singleton
if (!isset($this->tables[$idTable])) {
throw new TableNotFoundException(sprintf("This report has been reprocessed since your last click. To see this error less often, please increase the timeout value in seconds in Settings > General Settings. (error: id %s not found).", $idTable));
}
+
return $this->tables[$idTable];
}
@@ -86,6 +87,7 @@ class Manager extends Singleton
$this->deleteTable($id);
}
}
+
if ($deleteWhenIdTableGreaterThan == 0) {
$this->tables = array();
$this->nextTableId = 1;
diff --git a/core/DataTable/Map.php b/core/DataTable/Map.php
index dd8bec7ca5..8795eac134 100644
--- a/core/DataTable/Map.php
+++ b/core/DataTable/Map.php
@@ -246,12 +246,14 @@ class Map implements DataTableInterface
public function getColumn($name)
{
$values = array();
+
foreach ($this->getDataTables() as $table) {
$moreValues = $table->getColumn($name);
foreach ($moreValues as &$value) {
$values[] = $value;
}
}
+
return $values;
}
diff --git a/core/DataTable/Renderer.php b/core/DataTable/Renderer.php
index 5705b3e205..f825739611 100644
--- a/core/DataTable/Renderer.php
+++ b/core/DataTable/Renderer.php
@@ -161,13 +161,16 @@ abstract class Renderer extends BaseFactory
{
$className = ucfirst(strtolower($id));
$className = 'Piwik\DataTable\Renderer\\' . $className;
+
return $className;
}
protected static function getInvalidClassIdExceptionMessage($id)
{
$availableRenderers = implode(', ', self::getRenderers());
- return Piwik::translate('General_ExceptionInvalidRendererFormat', array(self::getClassNameFromClassId($id), $availableRenderers));
+ $klassName = self::getClassNameFromClassId($id);
+
+ return Piwik::translate('General_ExceptionInvalidRendererFormat', array($klassName, $availableRenderers));
}
/**
@@ -187,12 +190,15 @@ abstract class Renderer extends BaseFactory
$value = @mb_convert_encoding($value, 'UTF-8', 'UTF-8');
}
$value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8');
+
$htmlentities = array("&nbsp;", "&iexcl;", "&cent;", "&pound;", "&curren;", "&yen;", "&brvbar;", "&sect;", "&uml;", "&copy;", "&ordf;", "&laquo;", "&not;", "&shy;", "&reg;", "&macr;", "&deg;", "&plusmn;", "&sup2;", "&sup3;", "&acute;", "&micro;", "&para;", "&middot;", "&cedil;", "&sup1;", "&ordm;", "&raquo;", "&frac14;", "&frac12;", "&frac34;", "&iquest;", "&Agrave;", "&Aacute;", "&Acirc;", "&Atilde;", "&Auml;", "&Aring;", "&AElig;", "&Ccedil;", "&Egrave;", "&Eacute;", "&Ecirc;", "&Euml;", "&Igrave;", "&Iacute;", "&Icirc;", "&Iuml;", "&ETH;", "&Ntilde;", "&Ograve;", "&Oacute;", "&Ocirc;", "&Otilde;", "&Ouml;", "&times;", "&Oslash;", "&Ugrave;", "&Uacute;", "&Ucirc;", "&Uuml;", "&Yacute;", "&THORN;", "&szlig;", "&agrave;", "&aacute;", "&acirc;", "&atilde;", "&auml;", "&aring;", "&aelig;", "&ccedil;", "&egrave;", "&eacute;", "&ecirc;", "&euml;", "&igrave;", "&iacute;", "&icirc;", "&iuml;", "&eth;", "&ntilde;", "&ograve;", "&oacute;", "&ocirc;", "&otilde;", "&ouml;", "&divide;", "&oslash;", "&ugrave;", "&uacute;", "&ucirc;", "&uuml;", "&yacute;", "&thorn;", "&yuml;", "&euro;");
- $xmlentities = array("&#162;", "&#163;", "&#164;", "&#165;", "&#166;", "&#167;", "&#168;", "&#169;", "&#170;", "&#171;", "&#172;", "&#173;", "&#174;", "&#175;", "&#176;", "&#177;", "&#178;", "&#179;", "&#180;", "&#181;", "&#182;", "&#183;", "&#184;", "&#185;", "&#186;", "&#187;", "&#188;", "&#189;", "&#190;", "&#191;", "&#192;", "&#193;", "&#194;", "&#195;", "&#196;", "&#197;", "&#198;", "&#199;", "&#200;", "&#201;", "&#202;", "&#203;", "&#204;", "&#205;", "&#206;", "&#207;", "&#208;", "&#209;", "&#210;", "&#211;", "&#212;", "&#213;", "&#214;", "&#215;", "&#216;", "&#217;", "&#218;", "&#219;", "&#220;", "&#221;", "&#222;", "&#223;", "&#224;", "&#225;", "&#226;", "&#227;", "&#228;", "&#229;", "&#230;", "&#231;", "&#232;", "&#233;", "&#234;", "&#235;", "&#236;", "&#237;", "&#238;", "&#239;", "&#240;", "&#241;", "&#242;", "&#243;", "&#244;", "&#245;", "&#246;", "&#247;", "&#248;", "&#249;", "&#250;", "&#251;", "&#252;", "&#253;", "&#254;", "&#255;", "&#8364;");
- $value = str_replace($htmlentities, $xmlentities, $value);
+ $xmlentities = array("&#162;", "&#163;", "&#164;", "&#165;", "&#166;", "&#167;", "&#168;", "&#169;", "&#170;", "&#171;", "&#172;", "&#173;", "&#174;", "&#175;", "&#176;", "&#177;", "&#178;", "&#179;", "&#180;", "&#181;", "&#182;", "&#183;", "&#184;", "&#185;", "&#186;", "&#187;", "&#188;", "&#189;", "&#190;", "&#191;", "&#192;", "&#193;", "&#194;", "&#195;", "&#196;", "&#197;", "&#198;", "&#199;", "&#200;", "&#201;", "&#202;", "&#203;", "&#204;", "&#205;", "&#206;", "&#207;", "&#208;", "&#209;", "&#210;", "&#211;", "&#212;", "&#213;", "&#214;", "&#215;", "&#216;", "&#217;", "&#218;", "&#219;", "&#220;", "&#221;", "&#222;", "&#223;", "&#224;", "&#225;", "&#226;", "&#227;", "&#228;", "&#229;", "&#230;", "&#231;", "&#232;", "&#233;", "&#234;", "&#235;", "&#236;", "&#237;", "&#238;", "&#239;", "&#240;", "&#241;", "&#242;", "&#243;", "&#244;", "&#245;", "&#246;", "&#247;", "&#248;", "&#249;", "&#250;", "&#251;", "&#252;", "&#253;", "&#254;", "&#255;", "&#8364;");
+ $value = str_replace($htmlentities, $xmlentities, $value);
+
} elseif ($value === false) {
$value = 0;
}
+
return $value;
}
diff --git a/core/DataTable/Renderer/Console.php b/core/DataTable/Renderer/Console.php
index 88161165fc..0e1c127fb1 100644
--- a/core/DataTable/Renderer/Console.php
+++ b/core/DataTable/Renderer/Console.php
@@ -142,7 +142,7 @@ class Console extends Renderer
foreach ($metadata as $id => $metadataIn) {
$output .= "<br />";
$output .= $prefix . " <b>$id</b><br />";
- if(is_array($metadataIn)) {
+ if (is_array($metadataIn)) {
foreach ($metadataIn as $name => $value) {
$output .= $prefix . $prefix . "$name => $value";
}
diff --git a/core/DataTable/Renderer/Json.php b/core/DataTable/Renderer/Json.php
index 8fea38a658..f53ea60e67 100644
--- a/core/DataTable/Renderer/Json.php
+++ b/core/DataTable/Renderer/Json.php
@@ -11,7 +11,6 @@ namespace Piwik\DataTable\Renderer;
use Piwik\Common;
use Piwik\DataTable\Renderer;
use Piwik\DataTable;
-use Piwik\ProxyHttp;
/**
* JSON export.
diff --git a/core/DataTable/Row.php b/core/DataTable/Row.php
index 72e6d67b6c..07946adfd1 100644
--- a/core/DataTable/Row.php
+++ b/core/DataTable/Row.php
@@ -183,7 +183,7 @@ class Row implements \ArrayAccess, \IteratorAggregate
if (isset($this->c[self::COLUMNS][$oldName])) {
$this->c[self::COLUMNS][$newName] = $this->c[self::COLUMNS][$oldName];
}
- // outside the if() since we want to delete nulled columns
+ // outside the if () since we want to delete nulled columns
unset($this->c[self::COLUMNS][$oldName]);
}
@@ -500,9 +500,10 @@ class Row implements \ArrayAccess, \IteratorAggregate
*
* @param \Piwik\DataTable\Row $rowToSum The row to sum to this row.
* @param bool $enableCopyMetadata Whether metadata should be copied or not.
- * @param array $aggregationOperations for columns that should not be summed, determine which
+ * @param array|bool $aggregationOperations for columns that should not be summed, determine which
* aggregation should be used (min, max). format:
* `array('column name' => 'function name')`
+ * @throws Exception
*/
public function sumRow(Row $rowToSum, $enableCopyMetadata = true, $aggregationOperations = false)
{
@@ -528,7 +529,7 @@ class Row implements \ArrayAccess, \IteratorAggregate
if ($columnToSumName == Metrics::INDEX_MAX_ACTIONS) {
$operation = 'max';
}
- if(empty($operation)) {
+ if (empty($operation)) {
throw new Exception("Unknown aggregation operation for column $columnToSumName.");
}
diff --git a/core/Date.php b/core/Date.php
index c5b4cd7af6..5b8e0fab6e 100644
--- a/core/Date.php
+++ b/core/Date.php
@@ -273,15 +273,16 @@ class Date
// Unit tests pass (@see Date.test.php) but I'm pretty sure this is not the right way to do it
date_default_timezone_set($this->timezone);
$dtzone = timezone_open('UTC');
- $time = date('r', $this->timestamp);
- $dtime = date_create($time);
+ $time = date('r', $this->timestamp);
+ $dtime = date_create($time);
+
date_timezone_set($dtime, $dtzone);
- $dateWithTimezone = date_format($dtime, 'r');
+ $dateWithTimezone = date_format($dtime, 'r');
$dateWithoutTimezone = substr($dateWithTimezone, 0, -6);
- $timestamp = strtotime($dateWithoutTimezone);
+ $timestamp = strtotime($dateWithoutTimezone);
date_default_timezone_set('UTC');
- return (int)$timestamp;
+ return (int) $timestamp;
}
/**
diff --git a/core/Db.php b/core/Db.php
index e845a5ab6e..52c454e333 100644
--- a/core/Db.php
+++ b/core/Db.php
@@ -141,6 +141,7 @@ class Db
}
$profiler->queryEnd($q);
+
return $return;
}
@@ -271,7 +272,7 @@ class Db
*
* @param string $table The name of the table to delete from. Must be prefixed (see {@link Piwik\Common::prefixTable()}).
* @param string $where The where clause of the query. Must include the WHERE keyword.
- * @param $orderBy The column to order by and the order by direction, eg, `idvisit ASC`.
+ * @param string $orderBy The column to order by and the order by direction, eg, `idvisit ASC`.
* @param int $maxRowsPerQuery The maximum number of rows to delete per `DELETE` query.
* @param array $parameters Parameters to bind for each query.
* @return int The total number of rows deleted.
@@ -279,13 +280,13 @@ class Db
public static function deleteAllRows($table, $where, $orderBy, $maxRowsPerQuery = 100000, $parameters = array())
{
$orderByClause = $orderBy ? "ORDER BY $orderBy" : "";
- $sql = "DELETE FROM $table
- $where
- $orderByClause
+
+ $sql = "DELETE FROM $table $where $orderByClause
LIMIT " . (int)$maxRowsPerQuery;
// delete rows w/ a limit
$totalRowsDeleted = 0;
+
do {
$rowsDeleted = self::query($sql, $parameters)->rowCount();
@@ -308,6 +309,7 @@ class Db
public static function optimizeTables($tables)
{
$optimize = Config::getInstance()->General['enable_sql_optimize_queries'];
+
if (empty($optimize)) {
return;
}
@@ -315,13 +317,14 @@ class Db
if (empty($tables)) {
return false;
}
+
if (!is_array($tables)) {
$tables = array($tables);
}
// filter out all InnoDB tables
$myisamDbTables = array();
- foreach (Db::fetchAll("SHOW TABLE STATUS") as $row) {
+ foreach (self::getTableStatus() as $row) {
if (strtolower($row['Engine']) == 'myisam'
&& in_array($row['Name'], $tables)
) {
@@ -337,6 +340,11 @@ class Db
return self::query("OPTIMIZE TABLE " . implode(',', $myisamDbTables));
}
+ private static function getTableStatus()
+ {
+ return Db::fetchAll("SHOW TABLE STATUS");
+ }
+
/**
* Drops the supplied table or tables.
*
@@ -397,6 +405,7 @@ class Db
if (!is_array($tablesToRead)) {
$tablesToRead = array($tablesToRead);
}
+
if (!is_array($tablesToWrite)) {
$tablesToWrite = array($tablesToWrite);
}
@@ -405,6 +414,7 @@ class Db
foreach ($tablesToWrite as $table) {
$lockExprs[] = $table . " WRITE";
}
+
foreach ($tablesToRead as $table) {
$lockExprs[] = $table . " READ";
}
@@ -466,6 +476,7 @@ class Db
public static function segmentedFetchFirst($sql, $first, $last, $step, $params = array())
{
$result = false;
+
if ($step > 0) {
for ($i = $first; $result === false && $i <= $last; $i += $step) {
$result = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
@@ -475,6 +486,7 @@ class Db
$result = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
}
}
+
return $result;
}
@@ -502,6 +514,7 @@ class Db
public static function segmentedFetchOne($sql, $first, $last, $step, $params = array())
{
$result = array();
+
if ($step > 0) {
for ($i = $first; $i <= $last; $i += $step) {
$result[] = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
@@ -511,6 +524,7 @@ class Db
$result[] = self::fetchOne($sql, array_merge($params, array($i, $i + $step)));
}
}
+
return $result;
}
@@ -539,17 +553,19 @@ class Db
public static function segmentedFetchAll($sql, $first, $last, $step, $params = array())
{
$result = array();
+
if ($step > 0) {
for ($i = $first; $i <= $last; $i += $step) {
$currentParams = array_merge($params, array($i, $i + $step));
- $result = array_merge($result, self::fetchAll($sql, $currentParams));
+ $result = array_merge($result, self::fetchAll($sql, $currentParams));
}
} else {
for ($i = $first; $i >= $last; $i += $step) {
$currentParams = array_merge($params, array($i, $i + $step));
- $result = array_merge($result, self::fetchAll($sql, $currentParams));
+ $result = array_merge($result, self::fetchAll($sql, $currentParams));
}
}
+
return $result;
}
@@ -623,6 +639,7 @@ class Db
}
$maxRetries--;
}
+
return false;
}
diff --git a/core/Db/Adapter.php b/core/Db/Adapter.php
index 342a320d72..5ddd529caa 100644
--- a/core/Db/Adapter.php
+++ b/core/Db/Adapter.php
@@ -38,7 +38,7 @@ class Adapter
}
$className = self::getAdapterClassName($adapterName);
- $adapter = new $className($dbInfos);
+ $adapter = new $className($dbInfos);
if ($connect) {
$adapter->getConnection();
diff --git a/core/Db/Adapter/Mysqli.php b/core/Db/Adapter/Mysqli.php
index c864d057b0..fa74f35c9d 100644
--- a/core/Db/Adapter/Mysqli.php
+++ b/core/Db/Adapter/Mysqli.php
@@ -56,8 +56,9 @@ class Mysqli extends Zend_Db_Adapter_Mysqli implements AdapterInterface
*/
public function checkServerVersion()
{
- $serverVersion = $this->getServerVersion();
+ $serverVersion = $this->getServerVersion();
$requiredVersion = Config::getInstance()->General['minimum_mysql_version'];
+
if (version_compare($serverVersion, $requiredVersion) === -1) {
throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
}
@@ -72,6 +73,7 @@ class Mysqli extends Zend_Db_Adapter_Mysqli implements AdapterInterface
{
$serverVersion = $this->getServerVersion();
$clientVersion = $this->getClientVersion();
+
// incompatible change to DECIMAL implementation in 5.0.3
if (version_compare($serverVersion, '5.0.3') >= 0
&& version_compare($clientVersion, '5.0.3') < 0
@@ -168,10 +170,12 @@ class Mysqli extends Zend_Db_Adapter_Mysqli implements AdapterInterface
public function getClientVersion()
{
$this->_connect();
- $version = $this->_connection->server_version;
- $major = (int)($version / 10000);
- $minor = (int)($version % 10000 / 100);
+
+ $version = $this->_connection->server_version;
+ $major = (int)($version / 10000);
+ $minor = (int)($version % 10000 / 100);
$revision = (int)($version % 100);
+
return $major . '.' . $minor . '.' . $revision;
}
}
diff --git a/core/Db/Adapter/Pdo/Mssql.php b/core/Db/Adapter/Pdo/Mssql.php
index fd0248958e..ab032e145d 100644
--- a/core/Db/Adapter/Pdo/Mssql.php
+++ b/core/Db/Adapter/Pdo/Mssql.php
@@ -66,7 +66,7 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface
try {
$serverName = $this->_config["host"];
- $database = $this->_config["dbname"];
+ $database = $this->_config["dbname"];
if (is_null($database)) {
$database = 'master';
}
@@ -134,8 +134,9 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface
*/
public function checkServerVersion()
{
- $serverVersion = $this->getServerVersion();
+ $serverVersion = $this->getServerVersion();
$requiredVersion = Config::getInstance()->General['minimum_mssql_version'];
+
if (version_compare($serverVersion, $requiredVersion) === -1) {
throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MSSQL', $serverVersion, $requiredVersion)));
}
@@ -149,7 +150,7 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface
public function getServerVersion()
{
try {
- $stmt = $this->query("SELECT CAST(SERVERPROPERTY('productversion') as VARCHAR) as productversion");
+ $stmt = $this->query("SELECT CAST(SERVERPROPERTY('productversion') as VARCHAR) as productversion");
$result = $stmt->fetchAll(Zend_Db::FETCH_NUM);
if (count($result)) {
return $result[0][0];
@@ -169,6 +170,7 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface
{
$serverVersion = $this->getServerVersion();
$clientVersion = $this->getClientVersion();
+
if (version_compare($serverVersion, '10') >= 0
&& version_compare($clientVersion, '10') < 0
) {
@@ -224,6 +226,7 @@ class Mssql extends Zend_Db_Adapter_Pdo_Mssql implements AdapterInterface
if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) {
return $match[1] == $errno;
}
+
return false;
}
diff --git a/core/Db/Adapter/Pdo/Mysql.php b/core/Db/Adapter/Pdo/Mysql.php
index 59c3880bc9..3ed5ae9c0f 100644
--- a/core/Db/Adapter/Pdo/Mysql.php
+++ b/core/Db/Adapter/Pdo/Mysql.php
@@ -92,8 +92,9 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
*/
public function checkServerVersion()
{
- $serverVersion = $this->getServerVersion();
+ $serverVersion = $this->getServerVersion();
$requiredVersion = Config::getInstance()->General['minimum_mysql_version'];
+
if (version_compare($serverVersion, $requiredVersion) === -1) {
throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('MySQL', $serverVersion, $requiredVersion)));
}
@@ -108,6 +109,7 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
{
$serverVersion = $this->getServerVersion();
$clientVersion = $this->getClientVersion();
+
// incompatible change to DECIMAL implementation in 5.0.3
if (version_compare($serverVersion, '5.0.3') >= 0
&& version_compare($clientVersion, '5.0.3') < 0
@@ -159,6 +161,7 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
if (preg_match('/(?:\[|\s)([0-9]{4})(?:\]|\s)/', $e->getMessage(), $match)) {
return $match[1] == $errno;
}
+
return false;
}
@@ -170,9 +173,11 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
public function isConnectionUTF8()
{
$charsetInfo = $this->fetchAll('SHOW VARIABLES LIKE ?', array('character_set_connection'));
+
if (empty($charsetInfo)) {
return false;
}
+
$charset = $charsetInfo[0]['Value'];
return $charset === 'utf8';
}
diff --git a/core/Db/Adapter/Pdo/Pgsql.php b/core/Db/Adapter/Pdo/Pgsql.php
index c68280fe28..0a37ab2dc9 100644
--- a/core/Db/Adapter/Pdo/Pgsql.php
+++ b/core/Db/Adapter/Pdo/Pgsql.php
@@ -47,6 +47,7 @@ class Pgsql extends Zend_Db_Adapter_Pdo_Pgsql implements AdapterInterface
{
$databaseVersion = $this->getServerVersion();
$requiredVersion = Config::getInstance()->General['minimum_pgsql_version'];
+
if (version_compare($databaseVersion, $requiredVersion) === -1) {
throw new Exception(Piwik::translate('General_ExceptionDatabaseVersion', array('PostgreSQL', $databaseVersion, $requiredVersion)));
}
@@ -146,6 +147,7 @@ class Pgsql extends Zend_Db_Adapter_Pdo_Pgsql implements AdapterInterface
if (preg_match('/([0-9]{2}[0-9P][0-9]{2})/', $e->getMessage(), $match)) {
return $match[1] == $map[$errno];
}
+
return false;
}
diff --git a/core/Db/BatchInsert.php b/core/Db/BatchInsert.php
index 858dad4e38..f6e7a223c7 100644
--- a/core/Db/BatchInsert.php
+++ b/core/Db/BatchInsert.php
@@ -33,13 +33,12 @@ class BatchInsert
public static function tableInsertBatchIterate($tableName, $fields, $values, $ignoreWhenDuplicate = true)
{
$fieldList = '(' . join(',', $fields) . ')';
- $ignore = $ignoreWhenDuplicate ? 'IGNORE' : '';
+ $ignore = $ignoreWhenDuplicate ? 'IGNORE' : '';
foreach ($values as $row) {
- $query = "INSERT $ignore
- INTO " . $tableName . "
- $fieldList
- VALUES (" . Common::getSqlStringFieldsArray($row) . ")";
+ $query = "INSERT $ignore INTO " . $tableName . "
+ $fieldList
+ VALUES (" . Common::getSqlStringFieldsArray($row) . ")";
Db::query($query, $row);
}
}
@@ -123,7 +122,7 @@ class BatchInsert
{
// Chroot environment: prefix the path with the absolute chroot path
$chrootPath = Config::getInstance()->General['absolute_chroot_path'];
- if(!empty($chrootPath)) {
+ if (!empty($chrootPath)) {
$filePath = $chrootPath . $filePath;
}
@@ -172,7 +171,8 @@ class BatchInsert
* @see http://bugs.php.net/bug.php?id=54158
*/
$openBaseDir = ini_get('open_basedir');
- $safeMode = ini_get('safe_mode');
+ $safeMode = ini_get('safe_mode');
+
if (empty($openBaseDir) && empty($safeMode)) {
// php 5.x - LOAD DATA LOCAL INFILE is disabled if open_basedir restrictions or safe_mode enabled
$keywords[] = 'LOCAL ';
@@ -199,9 +199,11 @@ class BatchInsert
$exceptions[] = "\n Try #" . (count($exceptions) + 1) . ': ' . $queryStart . ": " . $message;
}
}
+
if (count($exceptions)) {
throw new Exception(implode(",", $exceptions));
}
+
return false;
}
@@ -218,8 +220,8 @@ class BatchInsert
// Set up CSV delimiters, quotes, etc
$delim = $fileSpec['delim'];
$quote = $fileSpec['quote'];
- $eol = $fileSpec['eol'];
- $null = $fileSpec['null'];
+ $eol = $fileSpec['eol'];
+ $null = $fileSpec['null'];
$escapespecial_cb = $fileSpec['escapespecial_cb'];
$fp = @fopen($filePath, 'wb');
@@ -246,6 +248,7 @@ class BatchInsert
throw new Exception('Error writing to the tmp file ' . $filePath);
}
}
+
fclose($fp);
@chmod($filePath, 0777);
diff --git a/core/Db/Schema.php b/core/Db/Schema.php
index e210de5835..796e71dca5 100644
--- a/core/Db/Schema.php
+++ b/core/Db/Schema.php
@@ -38,7 +38,7 @@ class Schema extends Singleton
private static function getSchemaClassName($schemaName)
{
// Upgrade from pre 2.0.4
- if(strtolower($schemaName) == 'myisam'
+ if (strtolower($schemaName) == 'myisam'
|| empty($schemaName)) {
$schemaName = self::DEFAULT_SCHEMA;
}
@@ -116,11 +116,11 @@ class Schema extends Singleton
*/
private function loadSchema()
{
- $config = Config::getInstance();
- $dbInfos = $config->database;
+ $config = Config::getInstance();
+ $dbInfos = $config->database;
$schemaName = trim($dbInfos['schema']);
- $className = self::getSchemaClassName($schemaName);
+ $className = self::getSchemaClassName($schemaName);
$this->schema = new $className();
}
@@ -134,6 +134,7 @@ class Schema extends Singleton
if ($this->schema === null) {
$this->loadSchema();
}
+
return $this->schema;
}
diff --git a/core/Db/Schema/Mysql.php b/core/Db/Schema/Mysql.php
index 43a42f167d..eb8c1f71f1 100644
--- a/core/Db/Schema/Mysql.php
+++ b/core/Db/Schema/Mysql.php
@@ -20,6 +20,8 @@ use Piwik\DbHelper;
*/
class Mysql implements SchemaInterface
{
+ private $tablesInstalled = null;
+
/**
* Is this MySQL storage engine available?
*
@@ -58,216 +60,213 @@ class Mysql implements SchemaInterface
$prefixTables = $this->getTablePrefix();
$tables = array(
- 'user' => "CREATE TABLE {$prefixTables}user (
- login VARCHAR(100) NOT NULL,
- password CHAR(32) NOT NULL,
- alias VARCHAR(45) NOT NULL,
- email VARCHAR(100) NOT NULL,
- token_auth CHAR(32) NOT NULL,
- superuser_access TINYINT(2) unsigned NOT NULL DEFAULT '0',
- date_registered TIMESTAMP NULL,
- PRIMARY KEY(login),
- UNIQUE KEY uniq_keytoken(token_auth)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'access' => "CREATE TABLE {$prefixTables}access (
- login VARCHAR(100) NOT NULL,
- idsite INTEGER UNSIGNED NOT NULL,
- access VARCHAR(10) NULL,
- PRIMARY KEY(login, idsite)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'site' => "CREATE TABLE {$prefixTables}site (
- idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
- name VARCHAR(90) NOT NULL,
- main_url VARCHAR(255) NOT NULL,
- ts_created TIMESTAMP NULL,
- ecommerce TINYINT DEFAULT 0,
- sitesearch TINYINT DEFAULT 1,
- sitesearch_keyword_parameters TEXT NOT NULL,
- sitesearch_category_parameters TEXT NOT NULL,
- timezone VARCHAR( 50 ) NOT NULL,
- currency CHAR( 3 ) NOT NULL,
- excluded_ips TEXT NOT NULL,
- excluded_parameters TEXT NOT NULL,
- excluded_user_agents TEXT NOT NULL,
- `group` VARCHAR(250) NOT NULL,
- `type` VARCHAR(255) NOT NULL,
- keep_url_fragment TINYINT NOT NULL DEFAULT 0,
- PRIMARY KEY(idsite)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'site_url' => "CREATE TABLE {$prefixTables}site_url (
- idsite INTEGER(10) UNSIGNED NOT NULL,
- url VARCHAR(255) NOT NULL,
- PRIMARY KEY(idsite, url)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'goal' => " CREATE TABLE `{$prefixTables}goal` (
- `idsite` int(11) NOT NULL,
- `idgoal` int(11) NOT NULL,
- `name` varchar(50) NOT NULL,
- `match_attribute` varchar(20) NOT NULL,
- `pattern` varchar(255) NOT NULL,
- `pattern_type` varchar(10) NOT NULL,
- `case_sensitive` tinyint(4) NOT NULL,
- `allow_multiple` tinyint(4) NOT NULL,
- `revenue` float NOT NULL,
- `deleted` tinyint(4) NOT NULL default '0',
- PRIMARY KEY (`idsite`,`idgoal`)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'logger_message' => "CREATE TABLE {$prefixTables}logger_message (
- idlogger_message INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ 'user' => "CREATE TABLE {$prefixTables}user (
+ login VARCHAR(100) NOT NULL,
+ password CHAR(32) NOT NULL,
+ alias VARCHAR(45) NOT NULL,
+ email VARCHAR(100) NOT NULL,
+ token_auth CHAR(32) NOT NULL,
+ superuser_access TINYINT(2) unsigned NOT NULL DEFAULT '0',
+ date_registered TIMESTAMP NULL,
+ PRIMARY KEY(login),
+ UNIQUE KEY uniq_keytoken(token_auth)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'access' => "CREATE TABLE {$prefixTables}access (
+ login VARCHAR(100) NOT NULL,
+ idsite INTEGER UNSIGNED NOT NULL,
+ access VARCHAR(10) NULL,
+ PRIMARY KEY(login, idsite)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'site' => "CREATE TABLE {$prefixTables}site (
+ idsite INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ name VARCHAR(90) NOT NULL,
+ main_url VARCHAR(255) NOT NULL,
+ ts_created TIMESTAMP NULL,
+ ecommerce TINYINT DEFAULT 0,
+ sitesearch TINYINT DEFAULT 1,
+ sitesearch_keyword_parameters TEXT NOT NULL,
+ sitesearch_category_parameters TEXT NOT NULL,
+ timezone VARCHAR( 50 ) NOT NULL,
+ currency CHAR( 3 ) NOT NULL,
+ excluded_ips TEXT NOT NULL,
+ excluded_parameters TEXT NOT NULL,
+ excluded_user_agents TEXT NOT NULL,
+ `group` VARCHAR(250) NOT NULL,
+ `type` VARCHAR(255) NOT NULL,
+ keep_url_fragment TINYINT NOT NULL DEFAULT 0,
+ PRIMARY KEY(idsite)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'site_url' => "CREATE TABLE {$prefixTables}site_url (
+ idsite INTEGER(10) UNSIGNED NOT NULL,
+ url VARCHAR(255) NOT NULL,
+ PRIMARY KEY(idsite, url)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'goal' => "CREATE TABLE `{$prefixTables}goal` (
+ `idsite` int(11) NOT NULL,
+ `idgoal` int(11) NOT NULL,
+ `name` varchar(50) NOT NULL,
+ `match_attribute` varchar(20) NOT NULL,
+ `pattern` varchar(255) NOT NULL,
+ `pattern_type` varchar(10) NOT NULL,
+ `case_sensitive` tinyint(4) NOT NULL,
+ `allow_multiple` tinyint(4) NOT NULL,
+ `revenue` float NOT NULL,
+ `deleted` tinyint(4) NOT NULL default '0',
+ PRIMARY KEY (`idsite`,`idgoal`)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'logger_message' => "CREATE TABLE {$prefixTables}logger_message (
+ idlogger_message INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
tag VARCHAR(50) NULL,
- timestamp TIMESTAMP NULL,
+ timestamp TIMESTAMP NULL,
level VARCHAR(16) NULL,
- message TEXT NULL,
- PRIMARY KEY(idlogger_message)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'log_action' => "CREATE TABLE {$prefixTables}log_action (
- idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
- name TEXT,
- hash INTEGER(10) UNSIGNED NOT NULL,
- type TINYINT UNSIGNED NULL,
- url_prefix TINYINT(2) NULL,
- PRIMARY KEY(idaction),
- INDEX index_type_hash (type, hash)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'log_visit' => "CREATE TABLE {$prefixTables}log_visit (
- idvisit INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
- idsite INTEGER(10) UNSIGNED NOT NULL,
- idvisitor BINARY(8) NOT NULL,
- visit_last_action_time DATETIME NOT NULL,
- config_id BINARY(8) NOT NULL,
- location_ip VARBINARY(16) NOT NULL,
- PRIMARY KEY(idvisit),
- INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time),
- INDEX index_idsite_datetime (idsite, visit_last_action_time),
- INDEX index_idsite_idvisitor (idsite, idvisitor)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
+ message TEXT NULL,
+ PRIMARY KEY(idlogger_message)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'log_action' => "CREATE TABLE {$prefixTables}log_action (
+ idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ name TEXT,
+ hash INTEGER(10) UNSIGNED NOT NULL,
+ type TINYINT UNSIGNED NULL,
+ url_prefix TINYINT(2) NULL,
+ PRIMARY KEY(idaction),
+ INDEX index_type_hash (type, hash)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'log_visit' => "CREATE TABLE {$prefixTables}log_visit (
+ idvisit INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ idsite INTEGER(10) UNSIGNED NOT NULL,
+ idvisitor BINARY(8) NOT NULL,
+ visit_last_action_time DATETIME NOT NULL,
+ config_id BINARY(8) NOT NULL,
+ location_ip VARBINARY(16) NOT NULL,
+ PRIMARY KEY(idvisit),
+ INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time),
+ INDEX index_idsite_datetime (idsite, visit_last_action_time),
+ INDEX index_idsite_idvisitor (idsite, idvisitor)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
'log_conversion_item' => "CREATE TABLE `{$prefixTables}log_conversion_item` (
- idsite int(10) UNSIGNED NOT NULL,
- idvisitor BINARY(8) NOT NULL,
- server_time DATETIME NOT NULL,
- idvisit INTEGER(10) UNSIGNED NOT NULL,
- idorder varchar(100) NOT NULL,
-
- idaction_sku INTEGER(10) UNSIGNED NOT NULL,
- idaction_name INTEGER(10) UNSIGNED NOT NULL,
- idaction_category INTEGER(10) UNSIGNED NOT NULL,
- idaction_category2 INTEGER(10) UNSIGNED NOT NULL,
- idaction_category3 INTEGER(10) UNSIGNED NOT NULL,
- idaction_category4 INTEGER(10) UNSIGNED NOT NULL,
- idaction_category5 INTEGER(10) UNSIGNED NOT NULL,
- price FLOAT NOT NULL,
- quantity INTEGER(10) UNSIGNED NOT NULL,
- deleted TINYINT(1) UNSIGNED NOT NULL,
-
- PRIMARY KEY(idvisit, idorder, idaction_sku),
- INDEX index_idsite_servertime ( idsite, server_time )
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` (
- idvisit int(10) unsigned NOT NULL,
- idsite int(10) unsigned NOT NULL,
- idvisitor BINARY(8) NOT NULL,
- server_time datetime NOT NULL,
- idaction_url int(11) default NULL,
- idlink_va int(11) default NULL,
- idgoal int(10) NOT NULL,
- buster int unsigned NOT NULL,
- idorder varchar(100) default NULL,
- items SMALLINT UNSIGNED DEFAULT NULL,
- url text NOT NULL,
-
- PRIMARY KEY (idvisit, idgoal, buster),
- UNIQUE KEY unique_idsite_idorder (idsite, idorder),
- INDEX index_idsite_datetime ( idsite, server_time )
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
+ idsite int(10) UNSIGNED NOT NULL,
+ idvisitor BINARY(8) NOT NULL,
+ server_time DATETIME NOT NULL,
+ idvisit INTEGER(10) UNSIGNED NOT NULL,
+ idorder varchar(100) NOT NULL,
+ idaction_sku INTEGER(10) UNSIGNED NOT NULL,
+ idaction_name INTEGER(10) UNSIGNED NOT NULL,
+ idaction_category INTEGER(10) UNSIGNED NOT NULL,
+ idaction_category2 INTEGER(10) UNSIGNED NOT NULL,
+ idaction_category3 INTEGER(10) UNSIGNED NOT NULL,
+ idaction_category4 INTEGER(10) UNSIGNED NOT NULL,
+ idaction_category5 INTEGER(10) UNSIGNED NOT NULL,
+ price FLOAT NOT NULL,
+ quantity INTEGER(10) UNSIGNED NOT NULL,
+ deleted TINYINT(1) UNSIGNED NOT NULL,
+ PRIMARY KEY(idvisit, idorder, idaction_sku),
+ INDEX index_idsite_servertime ( idsite, server_time )
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'log_conversion' => "CREATE TABLE `{$prefixTables}log_conversion` (
+ idvisit int(10) unsigned NOT NULL,
+ idsite int(10) unsigned NOT NULL,
+ idvisitor BINARY(8) NOT NULL,
+ server_time datetime NOT NULL,
+ idaction_url int(11) default NULL,
+ idlink_va int(11) default NULL,
+ idgoal int(10) NOT NULL,
+ buster int unsigned NOT NULL,
+ idorder varchar(100) default NULL,
+ items SMALLINT UNSIGNED DEFAULT NULL,
+ url text NOT NULL,
+ PRIMARY KEY (idvisit, idgoal, buster),
+ UNIQUE KEY unique_idsite_idorder (idsite, idorder),
+ INDEX index_idsite_datetime ( idsite, server_time )
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action (
- idlink_va INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
- idsite int(10) UNSIGNED NOT NULL,
- idvisitor BINARY(8) NOT NULL,
- idvisit INTEGER(10) UNSIGNED NOT NULL,
- idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0,
- idaction_name_ref INTEGER(10) UNSIGNED NOT NULL,
-
- custom_float FLOAT NULL DEFAULT NULL,
- PRIMARY KEY(idlink_va),
- INDEX index_idvisit(idvisit)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling (
- query TEXT NOT NULL,
- count INTEGER UNSIGNED NULL,
- sum_time_ms FLOAT NULL,
- UNIQUE KEY query(query(100))
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'option' => "CREATE TABLE `{$prefixTables}option` (
- option_name VARCHAR( 255 ) NOT NULL,
- option_value LONGTEXT NOT NULL,
- autoload TINYINT NOT NULL DEFAULT '1',
- PRIMARY KEY ( option_name ),
- INDEX autoload( autoload )
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'session' => "CREATE TABLE {$prefixTables}session (
- id VARCHAR( 255 ) NOT NULL,
- modified INTEGER,
- lifetime INTEGER,
- data TEXT,
- PRIMARY KEY ( id )
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric (
- idarchive INTEGER UNSIGNED NOT NULL,
- name VARCHAR(255) NOT NULL,
- idsite INTEGER UNSIGNED NULL,
- date1 DATE NULL,
- date2 DATE NULL,
- period TINYINT UNSIGNED NULL,
- ts_archived DATETIME NULL,
- value DOUBLE NULL,
- PRIMARY KEY(idarchive, name),
- INDEX index_idsite_dates_period(idsite, date1, date2, period, ts_archived),
- INDEX index_period_archived(period, ts_archived)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
-
- 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob (
- idarchive INTEGER UNSIGNED NOT NULL,
- name VARCHAR(255) NOT NULL,
- idsite INTEGER UNSIGNED NULL,
- date1 DATE NULL,
- date2 DATE NULL,
- period TINYINT UNSIGNED NULL,
- ts_archived DATETIME NULL,
- value MEDIUMBLOB NULL,
- PRIMARY KEY(idarchive, name),
- INDEX index_period_archived(period, ts_archived)
- ) ENGINE=$engine DEFAULT CHARSET=utf8
- ",
+ idlink_va INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
+ idsite int(10) UNSIGNED NOT NULL,
+ idvisitor BINARY(8) NOT NULL,
+ idvisit INTEGER(10) UNSIGNED NOT NULL,
+ idaction_url_ref INTEGER(10) UNSIGNED NULL DEFAULT 0,
+ idaction_name_ref INTEGER(10) UNSIGNED NOT NULL,
+ custom_float FLOAT NULL DEFAULT NULL,
+ PRIMARY KEY(idlink_va),
+ INDEX index_idvisit(idvisit)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'log_profiling' => "CREATE TABLE {$prefixTables}log_profiling (
+ query TEXT NOT NULL,
+ count INTEGER UNSIGNED NULL,
+ sum_time_ms FLOAT NULL,
+ UNIQUE KEY query(query(100))
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'option' => "CREATE TABLE `{$prefixTables}option` (
+ option_name VARCHAR( 255 ) NOT NULL,
+ option_value LONGTEXT NOT NULL,
+ autoload TINYINT NOT NULL DEFAULT '1',
+ PRIMARY KEY ( option_name ),
+ INDEX autoload( autoload )
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'session' => "CREATE TABLE {$prefixTables}session (
+ id VARCHAR( 255 ) NOT NULL,
+ modified INTEGER,
+ lifetime INTEGER,
+ data TEXT,
+ PRIMARY KEY ( id )
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'archive_numeric' => "CREATE TABLE {$prefixTables}archive_numeric (
+ idarchive INTEGER UNSIGNED NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ idsite INTEGER UNSIGNED NULL,
+ date1 DATE NULL,
+ date2 DATE NULL,
+ period TINYINT UNSIGNED NULL,
+ ts_archived DATETIME NULL,
+ value DOUBLE NULL,
+ PRIMARY KEY(idarchive, name),
+ INDEX index_idsite_dates_period(idsite, date1, date2, period, ts_archived),
+ INDEX index_period_archived(period, ts_archived)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
+
+ 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob (
+ idarchive INTEGER UNSIGNED NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ idsite INTEGER UNSIGNED NULL,
+ date1 DATE NULL,
+ date2 DATE NULL,
+ period TINYINT UNSIGNED NULL,
+ ts_archived DATETIME NULL,
+ value MEDIUMBLOB NULL,
+ PRIMARY KEY(idarchive, name),
+ INDEX index_period_archived(period, ts_archived)
+ ) ENGINE=$engine DEFAULT CHARSET=utf8
+ ",
);
+
return $tables;
}
@@ -297,17 +296,17 @@ class Mysql implements SchemaInterface
*/
public function getTablesNames()
{
- $aTables = array_keys($this->getTablesCreateSql());
+ $aTables = array_keys($this->getTablesCreateSql());
$prefixTables = $this->getTablePrefix();
+
$return = array();
foreach ($aTables as $table) {
$return[] = $prefixTables . $table;
}
+
return $return;
}
- private $tablesInstalled = null;
-
/**
* Get list of installed columns in a table
*
@@ -317,7 +316,7 @@ class Mysql implements SchemaInterface
*/
public function getTableColumns($tableName)
{
- $db = Db::get();
+ $db = $this->getDb();
$allColumns = $db->fetchAll("SHOW COLUMNS FROM . $tableName");
@@ -340,13 +339,11 @@ class Mysql implements SchemaInterface
if (is_null($this->tablesInstalled)
|| $forceReload === true
) {
- $db = Db::get();
- $prefixTables = $this->getTablePrefix();
- // '_' matches any character; force it to be literal
- $prefixTables = str_replace('_', '\_', $prefixTables);
+ $db = $this->getDb();
+ $prefixTables = $this->getTablePrefixEscaped();
- $allTables = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "%'");
+ $allTables = $this->getAllExistingTables($prefixTables);
// all the tables to be installed
$allMyTables = $this->getTablesNames();
@@ -356,12 +353,13 @@ class Mysql implements SchemaInterface
// at this point we have the static list of core tables, but let's add the monthly archive tables
$allArchiveNumeric = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_numeric%'");
- $allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_blob%'");
+ $allArchiveBlob = $db->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "archive_blob%'");
$allTablesReallyInstalled = array_merge($tablesInstalled, $allArchiveNumeric, $allArchiveBlob);
$this->tablesInstalled = $allTablesReallyInstalled;
}
+
return $this->tablesInstalled;
}
@@ -385,6 +383,7 @@ class Mysql implements SchemaInterface
if (is_null($dbName)) {
$dbName = $this->getDbName();
}
+
Db::exec("CREATE DATABASE IF NOT EXISTS " . $dbName . " DEFAULT CHARACTER SET utf8");
}
@@ -408,7 +407,7 @@ class Mysql implements SchemaInterface
} catch (Exception $e) {
// mysql code error 1050:table already exists
// see bug #153 https://github.com/piwik/piwik/issues/153
- if (!Db::get()->isErrNo($e, '1050')) {
+ if (!$this->getDb()->isErrNo($e, '1050')) {
throw $e;
}
}
@@ -428,7 +427,7 @@ class Mysql implements SchemaInterface
*/
public function createTables()
{
- $db = Db::get();
+ $db = $this->getDb();
$prefixTables = $this->getTablePrefix();
$tablesAlreadyInstalled = $this->getTablesInstalled();
@@ -451,9 +450,9 @@ class Mysql implements SchemaInterface
{
// The anonymous user is the user that is assigned by default
// note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin
- $db = Db::get();
+ $db = $this->getDb();
$db->query("INSERT IGNORE INTO " . Common::prefixTable("user") . "
- VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', 0, '" . Date::factory('now')->getDatetime() . "' );");
+ VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', 0, '" . Date::factory('now')->getDatetime() . "' );");
}
/**
@@ -461,15 +460,15 @@ class Mysql implements SchemaInterface
*/
public function truncateAllTables()
{
- $tablesAlreadyInstalled = $this->getTablesInstalled($forceReload = true);
- foreach ($tablesAlreadyInstalled as $table) {
+ $tables = $this->getAllExistingTables();
+ foreach ($tables as $table) {
Db::query("TRUNCATE `$table`");
}
}
private function getTablePrefix()
{
- $dbInfos = Db::getDatabaseConfig();
+ $dbInfos = Db::getDatabaseConfig();
$prefixTables = $dbInfos['tables_prefix'];
return $prefixTables;
@@ -478,10 +477,15 @@ class Mysql implements SchemaInterface
private function getTableEngine()
{
$dbInfos = Db::getDatabaseConfig();
- $engine = $dbInfos['type'];
+ $engine = $dbInfos['type'];
+
return $engine;
}
+ private function getDb(){
+ return Db::get();
+ }
+
private function getDbName()
{
$dbInfos = Db::getDatabaseConfig();
@@ -489,4 +493,21 @@ class Mysql implements SchemaInterface
return $dbName;
}
+
+ private function getAllExistingTables($prefixTables = false)
+ {
+ if (empty($prefixTables)) {
+ $prefixTables = $this->getTablePrefixEscaped();
+ }
+
+ return Db::get()->fetchCol("SHOW TABLES LIKE '" . $prefixTables . "%'");
+ }
+
+ private function getTablePrefixEscaped()
+ {
+ $prefixTables = $this->getTablePrefix();
+ // '_' matches any character; force it to be literal
+ $prefixTables = str_replace('_', '\_', $prefixTables);
+ return $prefixTables;
+ }
}
diff --git a/core/DeviceDetectorCache.php b/core/DeviceDetectorCache.php
index 2c637aa95e..ba65b82c9f 100644
--- a/core/DeviceDetectorCache.php
+++ b/core/DeviceDetectorCache.php
@@ -32,6 +32,7 @@ class DeviceDetectorCache extends CacheFile implements \DeviceDetector\Cache\Cac
if (empty($id)) {
return false;
}
+
$id = $this->cleanupId($id);
if (array_key_exists($id, self::$staticCache)) {
diff --git a/core/Error.php b/core/Error.php
index f7decbbc16..c56e3301a7 100644
--- a/core/Error.php
+++ b/core/Error.php
@@ -8,11 +8,6 @@
*/
namespace Piwik;
-use Piwik\Common;
-use Piwik\Log;
-use Piwik\Piwik;
-use Piwik\Version;
-
require_once PIWIK_INCLUDE_PATH . '/core/Log.php';
/**
diff --git a/core/EventDispatcher.php b/core/EventDispatcher.php
index d9d26b4377..d343da8beb 100644
--- a/core/EventDispatcher.php
+++ b/core/EventDispatcher.php
@@ -48,7 +48,7 @@ class EventDispatcher extends Singleton
/**
* Plugin\Manager instance used to get list of loaded plugins.
*
- * @var Piwik\Plugin\Manager
+ * @var \Piwik\Plugin\Manager
*/
private $pluginManager;
diff --git a/core/ExceptionHandler.php b/core/ExceptionHandler.php
index 0e526225d5..8566385070 100644
--- a/core/ExceptionHandler.php
+++ b/core/ExceptionHandler.php
@@ -9,7 +9,6 @@
namespace Piwik;
use Piwik\API\ResponseBuilder;
-use Piwik\Common;
use Piwik\Plugin;
/**
diff --git a/core/Filechecks.php b/core/Filechecks.php
index 33d0ed0696..3dc6d53195 100644
--- a/core/Filechecks.php
+++ b/core/Filechecks.php
@@ -43,7 +43,7 @@ class Filechecks
$directoryToCheck = PIWIK_USER_PATH . $directoryToCheck;
}
- if(strpos($directoryToCheck, '/tmp/') !== false) {
+ if (strpos($directoryToCheck, '/tmp/') !== false) {
$directoryToCheck = SettingsPiwik::rewriteTmpPathWithInstanceId($directoryToCheck);
}
@@ -87,9 +87,9 @@ class Filechecks
$directoryList = "<code>chown -R ". self::getUserAndGroup() ." " . $realpath . "</code><br />" . $directoryList;
}
- if(function_exists('shell_exec')) {
+ if (function_exists('shell_exec')) {
$currentUser = self::getUser();
- if(!empty($currentUser)) {
+ if (!empty($currentUser)) {
$optionalUserInfo = " (running as user '" . $currentUser . "')";
}
}
@@ -211,13 +211,13 @@ class Filechecks
private static function getUserAndGroup()
{
$user = self::getUser();
- if(!function_exists('shell_exec')) {
+ if (!function_exists('shell_exec')) {
return $user . ':' . $user;
}
$group = trim(shell_exec('groups '. $user .' | cut -f3 -d" "'));
- if(empty($group)) {
+ if (empty($group)) {
$group = 'www-data';
}
return $user . ':' . $group;
@@ -225,7 +225,7 @@ class Filechecks
private static function getUser()
{
- if(!function_exists('shell_exec')) {
+ if (!function_exists('shell_exec')) {
return 'www-data';
}
return trim(shell_exec('whoami'));
diff --git a/core/FrontController.php b/core/FrontController.php
index a50fb152ca..ce39922a2e 100644
--- a/core/FrontController.php
+++ b/core/FrontController.php
@@ -57,6 +57,7 @@ use Piwik\Plugins\CoreHome\Controller as CoreHomeController;
class FrontController extends Singleton
{
const DEFAULT_MODULE = 'CoreHome';
+
/**
* Set to false and the Front Controller will not dispatch the request
*
@@ -153,7 +154,8 @@ class FrontController extends Singleton
return array(new CoreHomeController(), 'renderReportWidget');
}
- if (!empty($action) && 'menu' === substr($action, 0, 4)) {
+ if (!empty($action) && Report::PREFIX_ACTION_IN_MENU === substr($action, 0, strlen(Report
+ ::PREFIX_ACTION_IN_MENU))) {
$reportAction = lcfirst(substr($action, 4)); // menuGetPageUrls => getPageUrls
$report = Report::factory($module, $reportAction);
@@ -508,22 +510,22 @@ class FrontController extends Singleton
protected function handleSSLRedirection()
{
// Specifically disable for the opt out iframe
- if(Piwik::getModule() == 'CoreAdminHome' && Piwik::getAction() == 'optOut') {
+ if (Piwik::getModule() == 'CoreAdminHome' && Piwik::getAction() == 'optOut') {
return;
}
// Disable Https for VisitorGenerator
- if(Piwik::getModule() == 'VisitorGenerator') {
+ if (Piwik::getModule() == 'VisitorGenerator') {
return;
}
- if(Common::isPhpCliMode()) {
+ if (Common::isPhpCliMode()) {
return;
}
// Only enable this feature after Piwik is already installed
- if(!SettingsPiwik::isPiwikInstalled()) {
+ if (!SettingsPiwik::isPiwikInstalled()) {
return;
}
// proceed only when force_ssl = 1
- if(!SettingsPiwik::isHttpsForced()) {
+ if (!SettingsPiwik::isHttpsForced()) {
return;
}
Url::redirectToHttps();
diff --git a/core/Http.php b/core/Http.php
index 537330716a..3643fc8553 100644
--- a/core/Http.php
+++ b/core/Http.php
@@ -165,7 +165,7 @@ class Http
$aUrl = trim($aUrl);
// other result data
- $status = null;
+ $status = null;
$headers = array();
if ($method == 'socket') {
@@ -183,7 +183,7 @@ class Http
throw new Exception('Invalid protocol/scheme: ' . $url['scheme']);
}
$host = $url['host'];
- $port = isset($url['port)']) ? $url['port'] : 80;
+ $port = isset($url['port']) ? $url['port'] : 80;
$path = isset($url['path']) ? $url['path'] : '/';
if (isset($url['query'])) {
$path .= '?' . $url['query'];
diff --git a/core/IP.php b/core/IP.php
index 52f5f7c23f..691bc7442c 100644
--- a/core/IP.php
+++ b/core/IP.php
@@ -370,7 +370,7 @@ class IP
{
$proxyIps = array();
$config = Config::getInstance()->General;
- if(isset($config['proxy_ips'])) {
+ if (isset($config['proxy_ips'])) {
$proxyIps = $config['proxy_ips'];
}
if (!is_array($proxyIps)) {
diff --git a/core/Log.php b/core/Log.php
index a860dd1989..b3421d13e1 100644
--- a/core/Log.php
+++ b/core/Log.php
@@ -452,7 +452,7 @@ class Log extends Singleton
if ($level == self::ERROR) {
$message = $this->getMessageFormattedScreen($level, $tag, $datetime, $message);
$this->writeErrorToStandardErrorOutput($message);
- if(!isset($this->writers['screen'])) {
+ if (!isset($this->writers['screen'])) {
echo $message;
}
}
@@ -599,7 +599,7 @@ class Log extends Singleton
*/
private function writeErrorToStandardErrorOutput($message)
{
- if(defined('PIWIK_TEST_MODE')) {
+ if (defined('PIWIK_TEST_MODE')) {
// do not log on stderr during tests (prevent display of errors in CI output)
return;
}
diff --git a/core/Mail.php b/core/Mail.php
index d26b08b154..179b1221ab 100644
--- a/core/Mail.php
+++ b/core/Mail.php
@@ -34,9 +34,13 @@ class Mail extends Zend_Mail
public function setDefaultFromPiwik()
{
$customLogo = new CustomLogo();
- $fromEmailName = $customLogo->isEnabled()
- ? Piwik::translate('CoreHome_WebAnalyticsReports')
- : Piwik::translate('ScheduledReports_PiwikReports');
+
+ if ($customLogo->isEnabled()) {
+ $fromEmailName = Piwik::translate('CoreHome_WebAnalyticsReports');
+ } else {
+ $fromEmailName = Piwik::translate('ScheduledReports_PiwikReports');
+ }
+
$fromEmailAddress = Config::getInstance()->General['noreply_email_address'];
$this->setFrom($fromEmailAddress, $fromEmailName);
}
@@ -77,20 +81,29 @@ class Mail extends Zend_Mail
private function initSmtpTransport()
{
$mailConfig = Config::getInstance()->mail;
+
if (empty($mailConfig['host'])
|| $mailConfig['transport'] != 'smtp'
) {
return;
}
+
$smtpConfig = array();
- if (!empty($mailConfig['type']))
+ if (!empty($mailConfig['type'])) {
$smtpConfig['auth'] = strtolower($mailConfig['type']);
- if (!empty($mailConfig['username']))
+ }
+
+ if (!empty($mailConfig['username'])) {
$smtpConfig['username'] = $mailConfig['username'];
- if (!empty($mailConfig['password']))
+ }
+
+ if (!empty($mailConfig['password'])) {
$smtpConfig['password'] = $mailConfig['password'];
- if (!empty($mailConfig['encryption']))
+ }
+
+ if (!empty($mailConfig['encryption'])) {
$smtpConfig['ssl'] = $mailConfig['encryption'];
+ }
$tr = new \Zend_Mail_Transport_Smtp($mailConfig['host'], $smtpConfig);
Mail::setDefaultTransport($tr);
@@ -112,12 +125,12 @@ class Mail extends Zend_Mail
*/
protected function parseDomainPlaceholderAsPiwikHostName($email)
{
- $hostname = Config::getInstance()->mail['defaultHostnameIfEmpty'];
+ $hostname = Config::getInstance()->mail['defaultHostnameIfEmpty'];
$piwikHost = Url::getCurrentHost($hostname);
// If known Piwik URL, use it instead of "localhost"
$piwikUrl = SettingsPiwik::getPiwikUrl();
- $url = parse_url($piwikUrl);
+ $url = parse_url($piwikUrl);
if ($this->isHostDefinedAndNotLocal($url)) {
$piwikHost = $url['host'];
}
diff --git a/core/Menu/Group.php b/core/Menu/Group.php
index 17e7c9bc5c..90e063e264 100644
--- a/core/Menu/Group.php
+++ b/core/Menu/Group.php
@@ -18,8 +18,8 @@ class Group
public function add($subTitleMenu, $url, $tooltip = false)
{
$this->items[] = array(
- 'name' => $subTitleMenu,
- 'url' => $url,
+ 'name' => $subTitleMenu,
+ 'url' => $url,
'tooltip' => $tooltip
);;
}
diff --git a/core/Menu/MenuAbstract.php b/core/Menu/MenuAbstract.php
index 1fe1152c98..0d77ff21d0 100644
--- a/core/Menu/MenuAbstract.php
+++ b/core/Menu/MenuAbstract.php
@@ -9,7 +9,6 @@
namespace Piwik\Menu;
use Piwik\Common;
-use Piwik\Log;
use Piwik\Plugins\SitesManager\API;
use Piwik\Singleton;
use Piwik\Plugin\Manager as PluginManager;
@@ -104,7 +103,7 @@ abstract class MenuAbstract extends Singleton
public function addItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false)
{
// make sure the idSite value used is numeric (hack-y fix for #3426)
- if (!is_numeric(Common::getRequestVar('idSite', false))) {
+ if (isset($url['idSite']) && !is_numeric($url['idSite'])) {
$idSites = API::getInstance()->getSitesIdWithAtLeastViewAccess();
$url['idSite'] = reset($idSites);
}
@@ -215,8 +214,8 @@ abstract class MenuAbstract extends Singleton
{
foreach ($this->edits as $edit) {
$mainMenuToEdit = $edit[0];
- $subMenuToEdit = $edit[1];
- $newUrl = $edit[2];
+ $subMenuToEdit = $edit[1];
+ $newUrl = $edit[2];
if ($subMenuToEdit === null) {
$menuDataToEdit = @$this->menu[$mainMenuToEdit];
@@ -236,14 +235,14 @@ abstract class MenuAbstract extends Singleton
{
foreach($this->menuEntriesToRemove as $menuToDelete) {
- if(empty($menuToDelete[1])) {
+ if (empty($menuToDelete[1])) {
// Delete Main Menu
- if(isset($this->menu[$menuToDelete[0]])) {
+ if (isset($this->menu[$menuToDelete[0]])) {
unset($this->menu[$menuToDelete[0]]);
}
} else {
// Delete Sub Menu
- if(isset($this->menu[$menuToDelete[0]][$menuToDelete[1]])) {
+ if (isset($this->menu[$menuToDelete[0]][$menuToDelete[1]])) {
unset($this->menu[$menuToDelete[0]][$menuToDelete[1]]);
}
}
@@ -256,9 +255,10 @@ abstract class MenuAbstract extends Singleton
{
foreach ($this->renames as $rename) {
$mainMenuOriginal = $rename[0];
- $subMenuOriginal = $rename[1];
- $mainMenuRenamed = $rename[2];
- $subMenuRenamed = $rename[3];
+ $subMenuOriginal = $rename[1];
+ $mainMenuRenamed = $rename[2];
+ $subMenuRenamed = $rename[3];
+
// Are we changing a submenu?
if (!empty($subMenuOriginal)) {
if (isset($this->menu[$mainMenuOriginal][$subMenuOriginal])) {
diff --git a/core/Menu/MenuAdmin.php b/core/Menu/MenuAdmin.php
index 5c814b6706..13e8480596 100644
--- a/core/Menu/MenuAdmin.php
+++ b/core/Menu/MenuAdmin.php
@@ -45,7 +45,9 @@ class MenuAdmin extends MenuAbstract
*/
public static function addEntry($adminMenuName, $url, $displayedForCurrentUser = true, $order = 20)
{
- self::getInstance()->add('General_Settings', $adminMenuName, $url, $displayedForCurrentUser, $order);
+ if ($displayedForCurrentUser) {
+ self::getInstance()->addItem('General_Settings', $adminMenuName, $url, $order);
+ }
}
/**
@@ -59,7 +61,7 @@ class MenuAdmin extends MenuAbstract
*/
public function addDevelopmentItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('CoreAdminHome_MenuDevelopment', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('CoreAdminHome_MenuDevelopment', $menuName, $url, $order, $tooltip);
}
/**
@@ -73,7 +75,7 @@ class MenuAdmin extends MenuAbstract
*/
public function addDiagnosticItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('CoreAdminHome_MenuDiagnostic', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('CoreAdminHome_MenuDiagnostic', $menuName, $url, $order, $tooltip);
}
/**
@@ -87,7 +89,7 @@ class MenuAdmin extends MenuAbstract
*/
public function addPlatformItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('CorePluginsAdmin_MenuPlatform', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('CorePluginsAdmin_MenuPlatform', $menuName, $url, $order, $tooltip);
}
/**
@@ -101,7 +103,7 @@ class MenuAdmin extends MenuAbstract
*/
public function addSettingsItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('General_Settings', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('General_Settings', $menuName, $url, $order, $tooltip);
}
/**
@@ -115,7 +117,7 @@ class MenuAdmin extends MenuAbstract
*/
public function addManageItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('CoreAdminHome_MenuManage', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('CoreAdminHome_MenuManage', $menuName, $url, $order, $tooltip);
}
/**
diff --git a/core/Menu/MenuMain.php b/core/Menu/MenuMain.php
index 5f27f3229e..adb6b538e9 100644
--- a/core/Menu/MenuMain.php
+++ b/core/Menu/MenuMain.php
@@ -11,7 +11,7 @@ namespace Piwik\Menu;
/**
* @deprecated since 2.4.0
* @see MenuReporting
- * @method static \Piwik\Menu\MenuMain getInstance()
+ * @method static MenuMain getInstance()
* @ignore
*/
class MenuMain extends MenuReporting
diff --git a/core/Menu/MenuReporting.php b/core/Menu/MenuReporting.php
index 28fddd8313..ac35151bcb 100644
--- a/core/Menu/MenuReporting.php
+++ b/core/Menu/MenuReporting.php
@@ -45,7 +45,7 @@ class MenuReporting extends MenuAbstract
*/
public function addVisitorsItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('General_Visitors', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('General_Visitors', $menuName, $url, $order, $tooltip);
}
/**
@@ -59,7 +59,7 @@ class MenuReporting extends MenuAbstract
*/
public function addActionsItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('General_Actions', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('General_Actions', $menuName, $url, $order, $tooltip);
}
/**
@@ -88,7 +88,7 @@ class MenuReporting extends MenuAbstract
*/
public function addReferrersItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('Referrers_Referrers', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('Referrers_Referrers', $menuName, $url, $order, $tooltip);
}
/**
diff --git a/core/Menu/MenuUser.php b/core/Menu/MenuUser.php
index ad9b195d46..758ac3d578 100755
--- a/core/Menu/MenuUser.php
+++ b/core/Menu/MenuUser.php
@@ -26,7 +26,7 @@ namespace Piwik\Menu;
* );
* }
*
- * @method static \Piwik\Menu\MenuUser getInstance()
+ * @method static MenuUser getInstance()
*/
class MenuUser extends MenuAbstract
{
@@ -42,7 +42,7 @@ class MenuUser extends MenuAbstract
*/
public function addManageItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('CoreAdminHome_MenuManage', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('CoreAdminHome_MenuManage', $menuName, $url, $order, $tooltip);
}
/**
@@ -56,7 +56,7 @@ class MenuUser extends MenuAbstract
*/
public function addPlatformItem($menuName, $url, $order = 50, $tooltip = false)
{
- $this->add('CorePluginsAdmin_MenuPlatform', $menuName, $url, true, $order, $tooltip);
+ $this->addItem('CorePluginsAdmin_MenuPlatform', $menuName, $url, $order, $tooltip);
}
/**
diff --git a/core/Metrics.php b/core/Metrics.php
index 03b9e2e094..0f8baf5a10 100644
--- a/core/Metrics.php
+++ b/core/Metrics.php
@@ -175,9 +175,11 @@ class Metrics
public static function getVisitsMetricNames()
{
$names = array();
+
foreach (self::$metricsAggregatedFromLogs as $metricId) {
$names[$metricId] = self::$mappingFromIdToName[$metricId];
}
+
return $names;
}
diff --git a/core/Metrics/Processed.php b/core/Metrics/Processed.php
index bbc638828a..0e6b7c969b 100644
--- a/core/Metrics/Processed.php
+++ b/core/Metrics/Processed.php
@@ -11,8 +11,6 @@ namespace Piwik\Metrics;
use Piwik\Metrics;
use Piwik\DataTable\Row;
use Piwik\DataTable;
-use Piwik\Piwik;
-use Piwik\Tracker\GoalManager;
class Processed extends Base
{
diff --git a/core/MetricsFormatter.php b/core/MetricsFormatter.php
index 8c58215e30..8554663525 100644
--- a/core/MetricsFormatter.php
+++ b/core/MetricsFormatter.php
@@ -61,10 +61,10 @@ class MetricsFormatter
// Display 01:45:17 time format
if ($displayTimeAsSentence === false) {
- $hours = floor($numberOfSeconds / 3600);
+ $hours = floor($numberOfSeconds / 3600);
$minutes = floor(($reminder = ($numberOfSeconds - $hours * 3600)) / 60);
$seconds = floor($reminder - $minutes * 60);
- $time = sprintf("%02s", $hours) . ':' . sprintf("%02s", $minutes) . ':' . sprintf("%02s", $seconds);
+ $time = sprintf("%02s", $hours) . ':' . sprintf("%02s", $minutes) . ':' . sprintf("%02s", $seconds);
$centiSeconds = ($numberOfSeconds * 100) % 100;
if ($centiSeconds) {
$time .= '.' . sprintf("%02s", $centiSeconds);
@@ -76,19 +76,19 @@ class MetricsFormatter
}
$secondsInYear = 86400 * 365.25;
- $years = floor($numberOfSeconds / $secondsInYear);
+ $years = floor($numberOfSeconds / $secondsInYear);
$minusYears = $numberOfSeconds - $years * $secondsInYear;
- $days = floor($minusYears / 86400);
+ $days = floor($minusYears / 86400);
$minusDays = $numberOfSeconds - $days * 86400;
- $hours = floor($minusDays / 3600);
+ $hours = floor($minusDays / 3600);
$minusDaysAndHours = $minusDays - $hours * 3600;
$minutes = floor($minusDaysAndHours / 60);
- $seconds = $minusDaysAndHours - $minutes * 60;
+ $seconds = $minusDaysAndHours - $minutes * 60;
$precision = ($seconds > 0 && $seconds < 0.01 ? 3 : 2);
- $seconds = round($seconds, $precision);
+ $seconds = round($seconds, $precision);
if ($years > 0) {
$return = sprintf(Piwik::translate('General_YearsDays'), $years, $days);
@@ -109,6 +109,7 @@ class MetricsFormatter
if ($isHtml) {
return str_replace(' ', '&nbsp;', $return);
}
+
return $return;
}
@@ -134,6 +135,7 @@ class MetricsFormatter
break;
}
}
+
return round($size, $precision) . " " . $currentUnit;
}
@@ -175,6 +177,7 @@ class MetricsFormatter
$value = sprintf("%01." . $precision . "f", $value);
}
}
+
$prettyMoney = $currencyBefore . $space . $value . $currencyAfter;
return $prettyMoney;
}
@@ -196,16 +199,19 @@ class MetricsFormatter
$timeAsSentence = (substr($columnName, -16) == '_time_generation');
return self::getPrettyTimeFromSeconds($value, $timeAsSentence);
}
+
// Add revenue symbol to revenues
if (strpos($columnName, 'revenue') !== false && strpos($columnName, 'evolution') === false) {
return self::getPrettyMoney($value, $idSite, $isHtml);
}
+
// Add % symbol to rates
if (strpos($columnName, '_rate') !== false) {
if (strpos($value, "%") === false) {
return $value . "%";
}
}
+
return $value;
}
@@ -217,12 +223,14 @@ class MetricsFormatter
*/
public static function getCurrencySymbol($idSite)
{
- $symbols = MetricsFormatter::getCurrencyList();
- $site = new Site($idSite);
+ $symbols = MetricsFormatter::getCurrencyList();
+ $site = new Site($idSite);
$currency = $site->getCurrency();
+
if (isset($symbols[$currency])) {
return $symbols[$currency][0];
}
+
return '';
}
@@ -235,10 +243,12 @@ class MetricsFormatter
public static function getCurrencyList()
{
static $currenciesList = null;
+
if (is_null($currenciesList)) {
require_once PIWIK_INCLUDE_PATH . '/core/DataFiles/Currencies.php';
$currenciesList = $GLOBALS['Piwik_CurrencyList'];
}
+
return $currenciesList;
}
}
diff --git a/core/Nonce.php b/core/Nonce.php
index 3937c4f94b..1b64b87933 100644
--- a/core/Nonce.php
+++ b/core/Nonce.php
@@ -160,7 +160,7 @@ class Nonce
* @param string $nonceName The nonce's unique ID. See {@link getNonce()}.
* @param string|null $nonce The nonce from the client. If `null`, the value from the
* **nonce** query parameter is used.
- * @throws Exception if the nonce is invalid. See {@link verifyNonce()}.
+ * @throws \Exception if the nonce is invalid. See {@link verifyNonce()}.
*/
public static function checkNonce($nonceName, $nonce = null)
{
diff --git a/core/Option.php b/core/Option.php
index c2c7816bba..c4410520ea 100644
--- a/core/Option.php
+++ b/core/Option.php
@@ -68,7 +68,7 @@ class Option
*/
public static function set($name, $value, $autoload = 0)
{
- return self::getInstance()->setValue($name, $value, $autoload);
+ self::getInstance()->setValue($name, $value, $autoload);
}
/**
@@ -79,7 +79,7 @@ class Option
*/
public static function delete($name, $value = null)
{
- return self::getInstance()->deleteValue($name, $value);
+ self::getInstance()->deleteValue($name, $value);
}
/**
@@ -91,7 +91,7 @@ class Option
*/
public static function deleteLike($namePattern, $value = null)
{
- return self::getInstance()->deleteNameLike($namePattern, $value);
+ self::getInstance()->deleteNameLike($namePattern, $value);
}
public static function clearCachedOption($name)
@@ -162,12 +162,13 @@ class Option
if (isset($this->all[$name])) {
return $this->all[$name];
}
- $value = Db::fetchOne('SELECT option_value ' .
- 'FROM `' . Common::prefixTable('option') . '` ' .
- 'WHERE option_name = ?', $name);
+ $value = Db::fetchOne('SELECT option_value FROM `' . Common::prefixTable('option') . '` ' .
+ 'WHERE option_name = ?', $name);
+
if ($value === false) {
return false;
}
+
$this->all[$name] = $value;
return $value;
}
@@ -184,11 +185,11 @@ class Option
protected function deleteValue($name, $value)
{
- $sql = 'DELETE FROM `' . Common::prefixTable('option') . '` WHERE option_name = ?';
+ $sql = 'DELETE FROM `' . Common::prefixTable('option') . '` WHERE option_name = ?';
$bind[] = $name;
if (isset($value)) {
- $sql .= ' AND option_value = ?';
+ $sql .= ' AND option_value = ?';
$bind[] = $value;
}
@@ -199,11 +200,11 @@ class Option
protected function deleteNameLike($name, $value = null)
{
- $sql = 'DELETE FROM `' . Common::prefixTable('option') . '` WHERE option_name LIKE ?';
+ $sql = 'DELETE FROM `' . Common::prefixTable('option') . '` WHERE option_name LIKE ?';
$bind[] = $name;
if (isset($value)) {
- $sql .= ' AND option_value = ?';
+ $sql .= ' AND option_value = ?';
$bind[] = $value;
}
@@ -214,7 +215,7 @@ class Option
protected function getNameLike($name)
{
- $sql = 'SELECT option_name, option_value FROM `' . Common::prefixTable('option') . '` WHERE option_name LIKE ?';
+ $sql = 'SELECT option_name, option_value FROM `' . Common::prefixTable('option') . '` WHERE option_name LIKE ?';
$bind = array($name);
$result = array();
@@ -235,9 +236,8 @@ class Option
return;
}
- $all = Db::fetchAll('SELECT option_value, option_name
- FROM `' . Common::prefixTable('option') . '`
- WHERE autoload = 1');
+ $all = Db::fetchAll('SELECT option_value, option_name FROM `' . Common::prefixTable('option') . '`
+ WHERE autoload = 1');
foreach ($all as $option) {
$this->all[$option['option_name']] = $option['option_value'];
}
diff --git a/core/Period.php b/core/Period.php
index 6898020ce7..1e3f188b5e 100644
--- a/core/Period.php
+++ b/core/Period.php
@@ -100,16 +100,20 @@ abstract class Period
public function getDateStart()
{
$this->generate();
+
if (count($this->subperiods) == 0) {
return $this->getDate();
}
+
$periods = $this->getSubperiods();
+
/** @var $currentPeriod Period */
$currentPeriod = $periods[0];
while ($currentPeriod->getNumberOfSubperiods() > 0) {
- $periods = $currentPeriod->getSubperiods();
+ $periods = $currentPeriod->getSubperiods();
$currentPeriod = $periods[0];
}
+
return $currentPeriod->getDate();
}
@@ -121,16 +125,20 @@ abstract class Period
public function getDateEnd()
{
$this->generate();
+
if (count($this->subperiods) == 0) {
return $this->getDate();
}
+
$periods = $this->getSubperiods();
+
/** @var $currentPeriod Period */
$currentPeriod = $periods[count($periods) - 1];
while ($currentPeriod->getNumberOfSubperiods() > 0) {
- $periods = $currentPeriod->getSubperiods();
+ $periods = $currentPeriod->getSubperiods();
$currentPeriod = $periods[count($periods) - 1];
}
+
return $currentPeriod->getDate();
}
@@ -212,10 +220,12 @@ abstract class Period
public function toString($format = "Y-m-d")
{
$this->generate();
+
$dateString = array();
foreach ($this->subperiods as $period) {
$dateString[] = $period->toString($format);
}
+
return $dateString;
}
@@ -259,6 +269,9 @@ abstract class Period
*/
public function getRangeString()
{
- return $this->getDateStart()->toString("Y-m-d") . "," . $this->getDateEnd()->toString("Y-m-d");
+ $dateStart = $this->getDateStart();
+ $dateEnd = $this->getDateEnd();
+
+ return $dateStart->toString("Y-m-d") . "," . $dateEnd->toString("Y-m-d");
}
}
diff --git a/core/Period/Day.php b/core/Period/Day.php
index 5f64627e45..b8212844cd 100644
--- a/core/Period/Day.php
+++ b/core/Period/Day.php
@@ -37,8 +37,10 @@ class Day extends Period
public function getLocalizedShortString()
{
//"Mon 15 Aug"
- $date = $this->getDateStart();
- $out = $date->getLocalized(Piwik::translate('CoreHome_ShortDateFormat'));
+ $date = $this->getDateStart();
+ $template = Piwik::translate('CoreHome_ShortDateFormat');
+
+ $out = $date->getLocalized($template);
return $out;
}
@@ -50,8 +52,9 @@ class Day extends Period
public function getLocalizedLongString()
{
//"Mon 15 Aug"
- $date = $this->getDateStart();
+ $date = $this->getDateStart();
$template = Piwik::translate('CoreHome_DateFormat');
+
$out = $date->getLocalized($template);
return $out;
}
diff --git a/core/Period/Factory.php b/core/Period/Factory.php
index b490956531..05303f40df 100644
--- a/core/Period/Factory.php
+++ b/core/Period/Factory.php
@@ -60,7 +60,7 @@ class Factory
public static function checkPeriodIsEnabled($period)
{
- if(!self::isPeriodEnabledForAPI($period)) {
+ if (!self::isPeriodEnabledForAPI($period)) {
self::throwExceptionInvalidPeriod($period);
}
}
diff --git a/core/Period/Month.php b/core/Period/Month.php
index 167280f15b..4609180980 100644
--- a/core/Period/Month.php
+++ b/core/Period/Month.php
@@ -60,6 +60,7 @@ class Month extends Period
if ($this->subperiodsProcessed) {
return;
}
+
parent::generate();
$date = $this->date;
diff --git a/core/Period/Range.php b/core/Period/Range.php
index 2ea6ab1792..36692dc0de 100644
--- a/core/Period/Range.php
+++ b/core/Period/Range.php
@@ -45,12 +45,14 @@ class Range extends Period
public function __construct($strPeriod, $strDate, $timezone = 'UTC', $today = false)
{
$this->strPeriod = $strPeriod;
- $this->strDate = $strDate;
+ $this->strDate = $strDate;
+ $this->timezone = $timezone;
$this->defaultEndDate = null;
- $this->timezone = $timezone;
+
if ($today === false) {
$today = Date::factory('now', $this->timezone);
}
+
$this->today = $today;
}
@@ -63,10 +65,12 @@ class Range extends Period
{
//"30 Dec 08 - 26 Feb 09"
$dateStart = $this->getDateStart();
- $dateEnd = $this->getDateEnd();
- $template = Piwik::translate('CoreHome_ShortDateFormatWithYear');
+ $dateEnd = $this->getDateEnd();
+ $template = Piwik::translate('CoreHome_ShortDateFormatWithYear');
+
$shortDateStart = $dateStart->getLocalized($template);
- $shortDateEnd = $dateEnd->getLocalized($template);
+ $shortDateEnd = $dateEnd->getLocalized($template);
+
$out = "$shortDateStart - $shortDateEnd";
return $out;
}
@@ -90,9 +94,11 @@ class Range extends Period
public function getDateStart()
{
$dateStart = parent::getDateStart();
+
if (empty($dateStart)) {
throw new Exception("Specified date range is invalid.");
}
+
return $dateStart;
}
@@ -203,10 +209,12 @@ class Range extends Period
} else {
throw new Exception(Piwik::translate('General_ExceptionInvalidDateRange', array($this->strDate, ' \'lastN\', \'previousN\', \'YYYY-MM-DD,YYYY-MM-DD\'')));
}
+
if ($this->strPeriod != 'range') {
$this->fillArraySubPeriods($startDate, $endDate, $this->strPeriod);
return;
}
+
$this->processOptimalSubperiods($startDate, $endDate);
// When period=range, we want End Date to be the actual specified end date,
// rather than the end of the month / week / whatever is used for processing this range
@@ -223,9 +231,11 @@ class Range extends Period
public static function parseDateRange($dateString)
{
$matched = preg_match('/^([0-9]{4}-[0-9]{1,2}-[0-9]{1,2}),(([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})|today|now|yesterday)$/D', trim($dateString), $regs);
+
if (empty($matched)) {
return false;
}
+
return $regs;
}
@@ -241,6 +251,7 @@ class Range extends Period
if (!is_null($this->endDate)) {
return $this->endDate;
}
+
return parent::getDateEnd();
}
@@ -425,7 +436,7 @@ class Range extends Period
* @param int $lastN The number of periods of type `$period` that the result range should
* span.
* @param string $endDate The desired end date of the range.
- * @param Site $site The site whose timezone should be used.
+ * @param \Piwik\Site $site The site whose timezone should be used.
* @return string The date range string, eg, `'2012-01-02,2013-01-02'`.
* @api
*/
@@ -434,6 +445,7 @@ class Range extends Period
$last30Relative = new Range($period, $lastN, $site->getTimezone());
$last30Relative->setDefaultEndDate(Date::factory($endDate));
$date = $last30Relative->getDateStart()->toString() . "," . $last30Relative->getDateEnd()->toString();
+
return $date;
}
diff --git a/core/Period/Week.php b/core/Period/Week.php
index f7b7264943..23f7ab32b6 100644
--- a/core/Period/Week.php
+++ b/core/Period/Week.php
@@ -26,7 +26,7 @@ class Week extends Period
{
//"30 Dec - 6 Jan 09"
$dateStart = $this->getDateStart();
- $dateEnd = $this->getDateEnd();
+ $dateEnd = $this->getDateEnd();
$string = Piwik::translate('CoreHome_ShortWeekFormat');
$string = self::getTranslatedRange($string, $dateStart, $dateEnd);
@@ -42,6 +42,7 @@ class Week extends Period
{
$format = Piwik::translate('CoreHome_LongWeekFormat');
$string = self::getTranslatedRange($format, $this->getDateStart(), $this->getDateEnd());
+
return Piwik::translate('CoreHome_PeriodWeek') . " " . $string;
}
@@ -58,6 +59,7 @@ class Week extends Period
$string = $dateStart->getLocalized($string);
$string = str_replace('To%', '%', $string);
$string = $dateEnd->getLocalized($string);
+
return $string;
}
@@ -68,10 +70,11 @@ class Week extends Period
*/
public function getPrettyString()
{
- $out = Piwik::translate('General_DateRangeFromTo',
- array($this->getDateStart()->toString(),
- $this->getDateEnd()->toString())
- );
+ $dateStart = $this->getDateStart();
+ $dateEnd = $this->getDateEnd();
+
+ $out = Piwik::translate('General_DateRangeFromTo', array($dateStart->toString(), $dateEnd->toString()));
+
return $out;
}
@@ -83,6 +86,7 @@ class Week extends Period
if ($this->subperiodsProcessed) {
return;
}
+
parent::generate();
$date = $this->date;
diff --git a/core/Period/Year.php b/core/Period/Year.php
index 3582161e3a..cb2d202ff7 100644
--- a/core/Period/Year.php
+++ b/core/Period/Year.php
@@ -58,6 +58,7 @@ class Year extends Period
if ($this->subperiodsProcessed) {
return;
}
+
parent::generate();
$year = $this->date->toString("Y");
@@ -78,10 +79,12 @@ class Year extends Period
function toString($format = 'ignored')
{
$this->generate();
+
$stringMonth = array();
foreach ($this->subperiods as $month) {
$stringMonth[] = $month->getDateStart()->toString("Y") . "-" . $month->getDateStart()->toString("m") . "-01";
}
+
return $stringMonth;
}
}
diff --git a/core/Piwik.php b/core/Piwik.php
index c00d5e42e9..16f50f387f 100644
--- a/core/Piwik.php
+++ b/core/Piwik.php
@@ -9,7 +9,6 @@
namespace Piwik;
use Exception;
-use Piwik\Common;
use Piwik\Db\Adapter;
use Piwik\Db\Schema;
use Piwik\Db;
@@ -135,7 +134,7 @@ class Piwik
// changes made to this code should be mirrored in plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js var generateJsCode
$jsCode = file_get_contents(PIWIK_INCLUDE_PATH . "/plugins/Morpheus/templates/javascriptCode.tpl");
$jsCode = htmlentities($jsCode);
- if(substr($piwikUrl, 0, 4) !== 'http') {
+ if (substr($piwikUrl, 0, 4) !== 'http') {
$piwikUrl = 'http://' . $piwikUrl;
}
preg_match('~^(http|https)://(.*)$~D', $piwikUrl, $matches);
@@ -428,7 +427,10 @@ class Piwik
* Helper method user to set the current as superuser.
* This should be used with great care as this gives the user all permissions.
*
+ * This method is deprecated, use {@link Access::doAsSuperUser()} instead.
+ *
* @param bool $bool true to set current user as Super User
+ * @deprecated
*/
public static function setUserHasSuperUserAccess($bool = true)
{
@@ -889,4 +891,4 @@ class Piwik
return $result;
}
-} \ No newline at end of file
+}
diff --git a/core/Plugin.php b/core/Plugin.php
index 8839a96212..55a70ab501 100644
--- a/core/Plugin.php
+++ b/core/Plugin.php
@@ -354,7 +354,7 @@ class Plugin
if ($this->cache->has()) {
$components = $this->cache->get();
- if($this->includeComponents($components)) {
+ if ($this->includeComponents($components)) {
return $components;
} else {
// problem including one cached file, refresh cache
diff --git a/core/Plugin/ComponentFactory.php b/core/Plugin/ComponentFactory.php
index 68415e9397..9cb9cd1c1b 100644
--- a/core/Plugin/ComponentFactory.php
+++ b/core/Plugin/ComponentFactory.php
@@ -74,7 +74,7 @@ class ComponentFactory
* @param callback $predicate
* @return mixed The component that satisfies $predicate or null if not found.
*/
- public static function getComponentIf($componentTypeClass, $pluginName, $predicate)
+ public static function getComponentif ($componentTypeClass, $pluginName, $predicate)
{
$pluginManager = PluginManager::getInstance();
diff --git a/core/Plugin/ConsoleCommand.php b/core/Plugin/ConsoleCommand.php
index 884b6f60ee..29e2fc65f6 100644
--- a/core/Plugin/ConsoleCommand.php
+++ b/core/Plugin/ConsoleCommand.php
@@ -8,7 +8,6 @@
*/
namespace Piwik\Plugin;
-use Piwik\Common;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
diff --git a/core/Plugin/Controller.php b/core/Plugin/Controller.php
index 3ebad0f1ed..0342e34724 100644
--- a/core/Plugin/Controller.php
+++ b/core/Plugin/Controller.php
@@ -834,32 +834,10 @@ abstract class Controller
public function redirectToIndex($moduleToRedirect, $actionToRedirect, $websiteId = null, $defaultPeriod = null,
$defaultDate = null, $parameters = array())
{
-
- $userPreferences = new UserPreferences();
-
- if (empty($websiteId)) {
- $websiteId = $userPreferences->getDefaultWebsiteId();
- }
- if (empty($defaultDate)) {
- $defaultDate = $userPreferences->getDefaultDate();
- }
- if (empty($defaultPeriod)) {
- $defaultPeriod = $userPreferences->getDefaultPeriod();
- }
- $parametersString = '';
- if (!empty($parameters)) {
- $parametersString = '&' . Url::getQueryStringFromParameters($parameters);
- }
-
- if ($websiteId) {
- $url = "index.php?module=" . $moduleToRedirect
- . "&action=" . $actionToRedirect
- . "&idSite=" . $websiteId
- . "&period=" . $defaultPeriod
- . "&date=" . $defaultDate
- . $parametersString;
- Url::redirectToUrl($url);
- exit;
+ try {
+ $this->doRedirectToUrl($moduleToRedirect, $actionToRedirect, $websiteId, $defaultPeriod, $defaultDate, $parameters);
+ } catch(Exception $e) {
+ // no website ID to default to, so could not redirect
}
if (Piwik::hasUserSuperUserAccess()) {
@@ -881,6 +859,7 @@ abstract class Controller
exit;
}
+
/**
* Checks that the token_auth in the URL matches the currently logged-in user's token_auth.
*
@@ -897,7 +876,7 @@ abstract class Controller
$tokenRequest = Common::getRequestVar('token_auth', false);
$tokenUser = Piwik::getCurrentUserTokenAuth();
- if(empty($tokenRequest) && empty($tokenUser)) {
+ if (empty($tokenRequest) && empty($tokenUser)) {
return; // UI tests
}
@@ -1000,4 +979,28 @@ abstract class Controller
Please check that you are logged in Piwik and have permission to access the specified website.");
}
}
+
+ /**
+ * @param $moduleToRedirect
+ * @param $actionToRedirect
+ * @param $websiteId
+ * @param $defaultPeriod
+ * @param $defaultDate
+ * @param $parameters
+ * @throws Exception
+ */
+ private function doRedirectToUrl($moduleToRedirect, $actionToRedirect, $websiteId, $defaultPeriod, $defaultDate, $parameters)
+ {
+ $menu = new Menu();
+
+ $parameters = array_merge(
+ $menu->urlForDefaultUserParams($websiteId, $defaultPeriod, $defaultDate),
+ $parameters
+ );
+ $queryParams = !empty($parameters) ? '&' . Url::getQueryStringFromParameters($parameters) : '';
+ $url = "index.php?module=%s&action=%s";
+ $url = sprintf($url, $moduleToRedirect, $actionToRedirect);
+ $url = $url . $queryParams;
+ Url::redirectToUrl($url);
+ }
}
diff --git a/core/Plugin/ControllerAdmin.php b/core/Plugin/ControllerAdmin.php
index b18fe67de6..e563e759f9 100644
--- a/core/Plugin/ControllerAdmin.php
+++ b/core/Plugin/ControllerAdmin.php
@@ -43,6 +43,7 @@ abstract class ControllerAdmin extends Controller
private static function notifyAnyInvalidPlugin()
{
$missingPlugins = \Piwik\Plugin\Manager::getInstance()->getMissingPlugins();
+
if (empty($missingPlugins)) {
return;
}
@@ -50,9 +51,11 @@ abstract class ControllerAdmin extends Controller
if (!Piwik::hasUserSuperUserAccess()) {
return;
}
+
$pluginsLink = Url::getCurrentQueryStringWithParametersModified(array(
'module' => 'CorePluginsAdmin', 'action' => 'plugins'
));
+
$invalidPluginsWarning = Piwik::translate('CoreAdminHome_InvalidPluginsWarning', array(
self::getPiwikVersion(),
'<strong>' . implode('</strong>,&nbsp;<strong>', $missingPlugins) . '</strong>'))
@@ -167,8 +170,10 @@ abstract class ControllerAdmin extends Controller
$view->currentAdminMenuName = MenuAdmin::getInstance()->getCurrentAdminMenuName();
$view->isDataPurgeSettingsEnabled = self::isDataPurgeSettingsEnabled();
- $view->enableFrames = PiwikConfig::getInstance()->General['enable_framed_settings'];
- if (!$view->enableFrames) {
+ $enableFrames = PiwikConfig::getInstance()->General['enable_framed_settings'];
+ $view->enableFrames = $enableFrames;
+
+ if (!$enableFrames) {
$view->setXFrameOptions('sameorigin');
}
@@ -192,6 +197,7 @@ abstract class ControllerAdmin extends Controller
$view->adminMenu = $adminMenu;
$notifications = $view->notifications;
+
if (empty($notifications)) {
$view->notifications = NotificationManager::getAllNotificationsToDisplay();
NotificationManager::cancelAllNonPersistent();
diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php
index eb234c1b6c..da2ba679df 100644
--- a/core/Plugin/Manager.php
+++ b/core/Plugin/Manager.php
@@ -10,8 +10,6 @@
namespace Piwik\Plugin;
use Piwik\Cache\PersistentCache;
-use Piwik\Cache\PluginAwareStaticCache;
-use Piwik\Cache\StaticCache;
use Piwik\CacheFile;
use Piwik\Common;
use Piwik\Config as PiwikConfig;
@@ -38,7 +36,7 @@ require_once PIWIK_INCLUDE_PATH . '/core/EventDispatcher.php';
/**
* The singleton that manages plugin loading/unloading and installation/uninstallation.
*
- * @method static \Piwik\Plugin\Manager getInstance()
+ * @method static Manager getInstance()
*/
class Manager extends Singleton
{
@@ -166,7 +164,7 @@ class Manager extends Singleton
public function isPluginOfficialAndNotBundledWithCore($pluginName)
{
static $gitModules;
- if(empty($gitModules)) {
+ if (empty($gitModules)) {
$gitModules = file_get_contents(PIWIK_INCLUDE_PATH . '/.gitmodules');
}
// All submodules are officially maintained plugins
@@ -541,7 +539,7 @@ class Manager extends Singleton
$listPlugins = array_unique($listPlugins);
foreach ($listPlugins as $pluginName) {
// Hide plugins that are never going to be used
- if($this->isPluginBogus($pluginName)) {
+ if ($this->isPluginBogus($pluginName)) {
continue;
}
@@ -609,15 +607,15 @@ class Manager extends Singleton
protected function isPluginThirdPartyAndBogus($pluginName)
{
- if($this->isPluginBundledWithCore($pluginName)) {
+ if ($this->isPluginBundledWithCore($pluginName)) {
return false;
}
- if($this->isPluginBogus($pluginName)) {
+ if ($this->isPluginBogus($pluginName)) {
return true;
}
$path = $this->getPluginsDirectory() . $pluginName;
- if(!$this->isManifestFileFound($path)) {
+ if (!$this->isManifestFileFound($path)) {
return true;
}
return false;
@@ -773,7 +771,7 @@ class Manager extends Singleton
$plugins = $this->getLoadedPlugins();
$enabled = $this->getActivatedPlugins();
- if(empty($enabled)) {
+ if (empty($enabled)) {
return array();
}
$enabled = array_combine($enabled, $enabled);
diff --git a/core/Plugin/Menu.php b/core/Plugin/Menu.php
index 6d2bdefccd..0cdc1878df 100644
--- a/core/Plugin/Menu.php
+++ b/core/Plugin/Menu.php
@@ -14,6 +14,7 @@ use Piwik\Menu\MenuReporting;
use Piwik\Menu\MenuTop;
use Piwik\Menu\MenuUser;
use Piwik\Plugin\Manager as PluginManager;
+use Piwik\Plugins\UsersManager\UserPreferences;
/**
* Base class of all plugin menu providers. Plugins that define their own menu items can extend this class to easily
@@ -105,8 +106,8 @@ class Menu
* @param string $module The name of the module/plugin the action belongs to. The module name is case sensitive.
* @param string $controllerAction The name of the action that should be executed within your controller
* @param array $additionalParams Optional URL parameters that will be appended to the URL
- * @return array|null Returns null if the given module is either not installed or not activated. Returns the URL
- * to the given module action otherwise.
+ * @return array|null Returns null if the given module is either not installed or not activated. Returns the array
+ * of query parameter names and values to the given module action otherwise.
*
* @since 2.7.0
* // not API for now
@@ -130,6 +131,80 @@ class Menu
}
/**
+ * Generates a URL to the given action of the current module, and it will also append some URL query parameters from the
+ * User preferences: idSite, period, date. If you do not need the parameters idSite, period and date to be generated
+ * use {@link urlForAction()} instead.
+ *
+ * @param string $controllerAction The name of the action that should be executed within your controller
+ * @param array $additionalParams Optional URL parameters that will be appended to the URL
+ * @return array Returns the array of query parameter names and values to the given module action and idSite date and period.
+ *
+ */
+ protected function urlForActionWithDefaultUserParams($controllerAction, $additionalParams = array())
+ {
+ $urlModuleAction = $this->urlForAction($controllerAction);
+ return array_merge(
+ $urlModuleAction,
+ $this->urlForDefaultUserParams(),
+ $additionalParams
+ );
+ }
+
+ /**
+ * Generates a URL to the given action of the given module, and it will also append some URL query parameters from the
+ * User preferences: idSite, period, date. If you do not need the parameters idSite, period and date to be generated
+ * use {@link urlForModuleAction()} instead.
+ *
+ * @param string $module The name of the module/plugin the action belongs to. The module name is case sensitive.
+ * @param string $controllerAction The name of the action that should be executed within your controller
+ * @param array $additionalParams Optional URL parameters that will be appended to the URL
+ * @return array|null Returns the array of query parameter names and values to the given module action and idSite date and period.
+ * Returns null if the module or action is invalid.
+ *
+ */
+ protected function urlForModuleActionWithDefaultUserParams($module, $controllerAction, $additionalParams = array())
+ {
+ $urlModuleAction = $this->urlForModuleAction($module, $controllerAction);
+ return array_merge(
+ $urlModuleAction,
+ $this->urlForDefaultUserParams(),
+ $additionalParams
+ );
+ }
+
+ /**
+ * Returns the &idSite=X&period=Y&date=Z query string fragment,
+ * fetched from current logged-in user's preferences.
+ *
+ * @param bool $websiteId
+ * @param bool $defaultPeriod
+ * @param bool $defaultDate
+ * @return string eg '&idSite=1&period=week&date=today'
+ * @throws \Exception in case a website was not specified and a default website id could not be found
+ */
+ public function urlForDefaultUserParams($websiteId = false, $defaultPeriod = false, $defaultDate = false)
+ {
+ $userPreferences = new UserPreferences();
+ if (empty($websiteId)) {
+ $websiteId = $userPreferences->getDefaultWebsiteId();
+ }
+ if (empty($websiteId)) {
+ throw new \Exception("A website ID was not specified and a website to default to could not be found.");
+ }
+ if (empty($defaultDate)) {
+ $defaultDate = $userPreferences->getDefaultDate();
+ }
+ if (empty($defaultPeriod)) {
+ $defaultPeriod = $userPreferences->getDefaultPeriod();
+ }
+ return array(
+ 'idSite' => $websiteId,
+ 'period' => $defaultPeriod,
+ 'date' => $defaultDate,
+ );
+ }
+
+ /**
* Configures the reporting menu which should only contain links to reports of a specific site such as
* "Search Engines", "Page Titles" or "Locations & Provider".
*/
diff --git a/core/Plugin/MetadataLoader.php b/core/Plugin/MetadataLoader.php
index 25f48ce0a0..34bb90dcdc 100644
--- a/core/Plugin/MetadataLoader.php
+++ b/core/Plugin/MetadataLoader.php
@@ -101,6 +101,7 @@ class MetadataLoader
) {
throw new Exception("Invalid JSON file: $path");
}
+
return $info;
}
}
diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php
index 6d950babd2..c30e731d46 100644
--- a/core/Plugin/Report.php
+++ b/core/Plugin/Report.php
@@ -42,6 +42,12 @@ class Report
const COMPONENT_SUBNAMESPACE = 'Reports';
/**
+ * When added to the menu, a given report eg 'getCampaigns'
+ * will be routed as &action=menuGetCampaigns
+ */
+ const PREFIX_ACTION_IN_MENU = 'menu';
+
+ /**
* The name of the module which is supposed to be equal to the name of the plugin. The module is detected
* automatically.
* @var string
@@ -328,11 +334,12 @@ class Report
{
if ($this->menuTitle) {
$action = $this->getMenuControllerAction();
- $menu->add($this->category,
- $this->menuTitle,
- array('module' => $this->module, 'action' => $action),
- $this->isEnabled(),
- $this->order);
+ if ($this->isEnabled()) {
+ $menu->addItem($this->category,
+ $this->menuTitle,
+ array('module' => $this->module, 'action' => $action),
+ $this->order);
+ }
}
}
@@ -710,7 +717,7 @@ class Report
private function getMenuControllerAction()
{
- return 'menu' . ucfirst($this->action);
+ return self::PREFIX_ACTION_IN_MENU . ucfirst($this->action);
}
private function getSubtableApiMethod()
@@ -731,7 +738,7 @@ class Report
*/
public static function getForDimension(Dimension $dimension)
{
- return ComponentFactory::getComponentIf(__CLASS__, $dimension->getModule(), function (Report $report) use ($dimension) {
+ return ComponentFactory::getComponentif (__CLASS__, $dimension->getModule(), function (Report $report) use ($dimension) {
return !$report->isSubtableReport()
&& $report->getDimension()
&& $report->getDimension()->getId() == $dimension->getId();
diff --git a/core/Plugin/ViewDataTable.php b/core/Plugin/ViewDataTable.php
index d7f72028cf..4f3b854127 100644
--- a/core/Plugin/ViewDataTable.php
+++ b/core/Plugin/ViewDataTable.php
@@ -275,11 +275,11 @@ abstract class ViewDataTable implements ViewInterface
protected function assignRelatedReportsTitle()
{
- if(!empty($this->config->related_reports_title)) {
+ if (!empty($this->config->related_reports_title)) {
// title already assigned by a plugin
return;
}
- if(count($this->config->related_reports) == 1) {
+ if (count($this->config->related_reports) == 1) {
$this->config->related_reports_title = Piwik::translate('General_RelatedReport') . ':';
} else {
$this->config->related_reports_title = Piwik::translate('General_RelatedReports') . ':';
diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php
index e82a8d7f73..e7e79c62e2 100644
--- a/core/Plugin/Visualization.php
+++ b/core/Plugin/Visualization.php
@@ -498,6 +498,7 @@ class Visualization extends ViewDataTable
'filter_excludelowpop',
'filter_excludelowpop_value',
);
+
foreach ($deleteFromJavascriptVariables as $name) {
if (isset($javascriptVariablesToSet[$name])) {
unset($javascriptVariablesToSet[$name]);
diff --git a/core/Profiler.php b/core/Profiler.php
index 7468b1f5d6..2b92583d89 100644
--- a/core/Profiler.php
+++ b/core/Profiler.php
@@ -143,7 +143,7 @@ class Profiler
{
$totalTime = self::getDbElapsedSecs();
$queryCount = Profiler::getQueryCount();
- if($queryCount > 0) {
+ if ($queryCount > 0) {
Log::debug(sprintf("Total queries = %d (total sql time = %.2fs)", $queryCount, $totalTime));
}
}
@@ -234,7 +234,7 @@ class Profiler
$currentGitBranch = SettingsPiwik::getCurrentGitBranch();
$profilerNamespace = "piwik";
- if($currentGitBranch != 'master') {
+ if ($currentGitBranch != 'master') {
$profilerNamespace .= "." . $currentGitBranch;
}
@@ -326,7 +326,7 @@ class Profiler
{
$runIds = file_get_contents( self::getPathToXHProfRunIds() );
$array = json_decode($runIds, $assoc = true);
- if(!is_array($array)) {
+ if (!is_array($array)) {
$array = array();
}
return $array;
diff --git a/core/ProxyHttp.php b/core/ProxyHttp.php
index a1e7fda32c..69f7144b44 100644
--- a/core/ProxyHttp.php
+++ b/core/ProxyHttp.php
@@ -233,6 +233,7 @@ class ProxyHttp
// FastCGI
$key = 'Status:';
}
+
Common::sendHeader($key . ' ' . $status);
}
diff --git a/core/RankingQuery.php b/core/RankingQuery.php
index f40ae1273e..f44845f075 100644
--- a/core/RankingQuery.php
+++ b/core/RankingQuery.php
@@ -215,7 +215,7 @@ class RankingQuery
public function execute($innerQuery, $bind = array())
{
$query = $this->generateQuery($innerQuery);
- $data = Db::fetchAll($query, $bind);
+ $data = Db::fetchAll($query, $bind);
if ($this->columnToMarkExcludedRows !== false) {
// split the result into the regular result and the rows with special treatment
diff --git a/core/Registry.php b/core/Registry.php
index a3383a84c0..5022bf09e2 100644
--- a/core/Registry.php
+++ b/core/Registry.php
@@ -11,7 +11,8 @@ namespace Piwik;
/**
* Registry class.
*
- * @method static \Piwik\Registry getInstance()
+ * @method static Registry getInstance()
+ * @api
*/
class Registry extends Singleton
{
diff --git a/core/ReportRenderer/Csv.php b/core/ReportRenderer/Csv.php
index 23ace2ac4d..94eca5172a 100644
--- a/core/ReportRenderer/Csv.php
+++ b/core/ReportRenderer/Csv.php
@@ -105,7 +105,7 @@ class Csv extends ReportRenderer
);
$reportData = $csvRenderer->render($processedReport);
- if(empty($reportData)) {
+ if (empty($reportData)) {
$reportData = Piwik::translate('CoreHome_ThereIsNoDataForThisReport');
}
diff --git a/core/ScheduledTask.php b/core/ScheduledTask.php
index 15b702dab8..d570f5bea2 100644
--- a/core/ScheduledTask.php
+++ b/core/ScheduledTask.php
@@ -77,6 +77,7 @@ class ScheduledTask
* should be executed and how long before the next execution.
* @param int $priority The priority of the task. Tasks with a higher priority will be executed first.
* Tasks with low priority will be executed last.
+ * @throws Exception
*/
public function __construct($objectInstance, $methodName, $methodParameter, $scheduledTime,
$priority = self::NORMAL_PRIORITY)
diff --git a/core/ScheduledTime.php b/core/ScheduledTime.php
index f221a82a43..3da4afe98b 100644
--- a/core/ScheduledTime.php
+++ b/core/ScheduledTime.php
@@ -183,7 +183,7 @@ abstract class ScheduledTime
* and a string description of the day within the period to execute the task on.
*
* @param string $periodType The scheduled period type. Can be `'hourly'`, `'daily'`, `'weekly'`, or `'monthly'`.
- * @param string|int|false $periodDay A string describing the day within the scheduled period to execute
+ * @param bool|false|int|string $periodDay A string describing the day within the scheduled period to execute
* the task on. Only valid for week and month periods.
*
* If `'weekly'` is supplied for `$periodType`, this should be a day
@@ -192,6 +192,7 @@ abstract class ScheduledTime
* If `'monthly'` is supplied for `$periodType`, this can be a numeric
* day in the month or a day in one week of the month. For example,
* `12`, `23`, `'first sunday'` or `'fourth tuesday'`.
+ * @throws Exception
* @api
*/
public static function factory($periodType, $periodDay = false)
@@ -203,13 +204,13 @@ abstract class ScheduledTime
return new Daily();
case 'weekly':
$result = new Weekly();
- if($periodDay !== false) {
+ if ($periodDay !== false) {
$result->setDay($periodDay);
}
return $result;
case 'monthly':
$result = new Monthly($periodDay);
- if($periodDay !== false) {
+ if ($periodDay !== false) {
if (is_int($periodDay)) {
$result->setDay($periodDay);
} else {
diff --git a/core/ScheduledTime/Monthly.php b/core/ScheduledTime/Monthly.php
index d4900530e8..84189f9bad 100644
--- a/core/ScheduledTime/Monthly.php
+++ b/core/ScheduledTime/Monthly.php
@@ -46,7 +46,6 @@ class Monthly extends ScheduledTime
$day = Weekly::getDayIntFromString($dayNumberString) % 7;
// get week number
- $week = false;
$weekNumberString = strtolower($weekNumberString);
if (isset(self::$weekNumberStringToInt[$weekNumberString])) {
$week = self::$weekNumberStringToInt[$weekNumberString];
diff --git a/core/Segment.php b/core/Segment.php
index e12b165028..420946794d 100644
--- a/core/Segment.php
+++ b/core/Segment.php
@@ -70,6 +70,7 @@ class Segment
* @param string $segmentCondition The segment condition, eg, `'browserCode=ff;countryCode=CA'`.
* @param array $idSites The list of sites the segment will be used with. Some segments are
* dependent on the site, such as goal segments.
+ * @throws Exception
*/
public function __construct($segmentCondition, $idSites)
{
@@ -99,7 +100,7 @@ class Segment
// As a preventive measure, we restrict the filter size to a safe limit
$string = substr($string, 0, self::SEGMENT_TRUNCATE_LIMIT);
- $this->string = $string;
+ $this->string = $string;
$this->idSites = $idSites;
$segment = new SegmentExpression($string);
$this->segment = $segment;
@@ -117,6 +118,7 @@ class Segment
$expression[SegmentExpression::INDEX_OPERAND] = $cleanedExpression;
$cleanedExpressions[] = $expression;
}
+
$segment->setSubExpressionsAfterCleanup($cleanedExpressions);
}
@@ -155,10 +157,10 @@ class Segment
throw new Exception("You do not have enough permission to access the segment " . $name);
}
- if($matchType != SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY
+ if ($matchType != SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY
&& $matchType != SegmentExpression::MATCH_IS_NULL_OR_EMPTY) {
- if(isset($segment['sqlFilterValue'])) {
+ if (isset($segment['sqlFilterValue'])) {
$value = call_user_func($segment['sqlFilterValue'], $value);
}
diff --git a/core/SegmentExpression.php b/core/SegmentExpression.php
index 682aa201d0..cbd2a3eb9e 100644
--- a/core/SegmentExpression.php
+++ b/core/SegmentExpression.php
@@ -84,7 +84,7 @@ class SegmentExpression
}
$leftMember = $matches[1];
- $operation = $matches[2];
+ $operation = $matches[2];
$valueRightMember = urldecode($matches[3]);
// is null / is not null
@@ -138,6 +138,7 @@ class SegmentExpression
if ($operand[1] !== null) {
$this->valuesBind[] = $operand[1];
}
+
$operand = $operand[0];
$sqlSubExpressions[] = array(
self::INDEX_BOOL_OPERATOR => $operator,
@@ -161,9 +162,9 @@ class SegmentExpression
*/
protected function getSqlMatchFromDefinition($def, &$availableTables)
{
- $field = $def[0];
+ $field = $def[0];
$matchType = $def[1];
- $value = $def[2];
+ $value = $def[2];
$alsoMatchNULLValues = false;
switch ($matchType) {
@@ -188,22 +189,22 @@ class SegmentExpression
break;
case self::MATCH_CONTAINS:
$sqlMatch = 'LIKE';
- $value = '%' . $this->escapeLikeString($value) . '%';
+ $value = '%' . $this->escapeLikeString($value) . '%';
break;
case self::MATCH_DOES_NOT_CONTAIN:
$sqlMatch = 'NOT LIKE';
- $value = '%' . $this->escapeLikeString($value) . '%';
+ $value = '%' . $this->escapeLikeString($value) . '%';
$alsoMatchNULLValues = true;
break;
case self::MATCH_IS_NOT_NULL_NOR_EMPTY:
$sqlMatch = 'IS NOT NULL AND (' . $field . ' <> \'\' OR ' . $field . ' = 0)';
- $value = null;
+ $value = null;
break;
case self::MATCH_IS_NULL_OR_EMPTY:
$sqlMatch = 'IS NULL OR ' . $field . ' = \'\' ';
- $value = null;
+ $value = null;
break;
case self::MATCH_ACTIONS_CONTAINS:
@@ -212,7 +213,7 @@ class SegmentExpression
// it can be used internally to inject sub-expressions into the query.
// see Segment::getCleanedExpression()
$sqlMatch = 'IN (' . $value['SQL'] . ')';
- $value = $this->escapeLikeString($value['bind']);
+ $value = $this->escapeLikeString($value['bind']);
break;
default:
throw new Exception("Filter contains the match type '" . $matchType . "' which is not supported");
diff --git a/core/Session/SaveHandler/DbTable.php b/core/Session/SaveHandler/DbTable.php
index 249faca6e1..74fc5e388b 100644
--- a/core/Session/SaveHandler/DbTable.php
+++ b/core/Session/SaveHandler/DbTable.php
@@ -119,8 +119,7 @@ class DbTable implements Zend_Session_SaveHandler_Interface
*/
public function destroy($id)
{
- $sql = 'DELETE FROM ' . $this->config['name']
- . ' WHERE ' . $this->config['primary'] . ' = ?';
+ $sql = 'DELETE FROM ' . $this->config['name'] . ' WHERE ' . $this->config['primary'] . ' = ?';
Db::get()->query($sql, array($id));
diff --git a/core/SettingsPiwik.php b/core/SettingsPiwik.php
index 4e83190e70..26949c560a 100644
--- a/core/SettingsPiwik.php
+++ b/core/SettingsPiwik.php
@@ -179,7 +179,7 @@ class SettingsPiwik
$url = $currentUrl;
}
- if(ProxyHttp::isHttps()) {
+ if (ProxyHttp::isHttps()) {
$url = str_replace("http://", "https://", $url);
}
return $url;
@@ -195,7 +195,7 @@ class SettingsPiwik
$exists = file_exists($config);
// Piwik is installed if the config file is found
- if(!$exists) {
+ if (!$exists) {
return false;
}
@@ -205,12 +205,12 @@ class SettingsPiwik
if (array_key_exists('installation_in_progress', $general)) {
$isInstallationInProgress = (bool) $general['installation_in_progress'];
}
- if($isInstallationInProgress) {
+ if ($isInstallationInProgress) {
return false;
}
// Check that the database section is really set, ie. file is not empty
- if(empty(Config::getInstance()->database['username'])) {
+ if (empty(Config::getInstance()->database['username'])) {
return false;
}
return true;
@@ -297,6 +297,8 @@ class SettingsPiwik
* this will return false..
*
* @param $piwikServerUrl
+ * @param bool $acceptInvalidSSLCertificates
+ * @throws Exception
* @return bool
*/
public static function checkPiwikServerWorking($piwikServerUrl, $acceptInvalidSSLCertificates = false)
@@ -337,7 +339,7 @@ class SettingsPiwik
public static function getCurrentGitBranch()
{
$file = PIWIK_INCLUDE_PATH . '/.git/HEAD';
- if(!file_exists($file)) {
+ if (!file_exists($file)) {
return '';
}
$firstLineOfGitHead = file($file);
@@ -385,19 +387,19 @@ class SettingsPiwik
protected static function getPiwikInstanceId()
{
// until Piwik is installed, we use hostname as instance_id
- if(!self::isPiwikInstalled()
+ if (!self::isPiwikInstalled()
&& Common::isPhpCliMode()) {
// enterprise:install use case
return Config::getHostname();
}
// config.ini.php not ready yet, instance_id will not be set
- if(!Config::getInstance()->existsLocalConfig()) {
+ if (!Config::getInstance()->existsLocalConfig()) {
return false;
}
$instanceId = @Config::getInstance()->General['instance_id'];
- if(!empty($instanceId)) {
+ if (!empty($instanceId)) {
return $instanceId;
}
diff --git a/core/Site.php b/core/Site.php
index 7efeee77ef..96b7316b76 100644
--- a/core/Site.php
+++ b/core/Site.php
@@ -94,7 +94,7 @@ class Site
*/
protected static function setSite($idSite, $infoSite)
{
- if(empty($idSite) || empty($infoSite)) {
+ if (empty($idSite) || empty($infoSite)) {
throw new Exception("An unexpected website was found, check idSite in the request.");
}
@@ -361,7 +361,7 @@ class Site
return API::getInstance()->getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin);
}
- if(is_bool($ids)) {
+ if (is_bool($ids)) {
return array();
}
if (!is_array($ids)) {
@@ -406,7 +406,7 @@ class Site
$site = API::getInstance()->getSiteFromId($idsite);
self::setSite($idsite, $site);
}
- if($field) {
+ if ($field) {
return self::$infoSites[$idsite][$field];
}
return self::$infoSites[$idsite];
diff --git a/core/Theme.php b/core/Theme.php
index b0fe5abecb..f7f85a5291 100644
--- a/core/Theme.php
+++ b/core/Theme.php
@@ -64,7 +64,7 @@ class Theme
return false;
}
$jsFiles = $info['javascript'];
- if(!is_array($jsFiles)) {
+ if (!is_array($jsFiles)) {
$jsFiles = array($jsFiles);
}
foreach($jsFiles as &$jsFile) {
@@ -107,18 +107,18 @@ class Theme
// Basic health check, we dont replace if not starting with plugins/
$posPluginsInPath = strpos($pathAsset, 'plugins');
- if( $posPluginsInPath !== 0) {
+ if ( $posPluginsInPath !== 0) {
return $source;
}
// or if it's already rewritten
- if(strpos($pathAsset, $this->themeName) !== false) {
+ if (strpos($pathAsset, $this->themeName) !== false) {
return $source;
}
$pathPluginName = substr($pathAsset, strlen('plugins/'));
$nextSlash = strpos($pathPluginName, '/');
- if($nextSlash === false) {
+ if ($nextSlash === false) {
return $source;
}
$pathPluginName = substr($pathPluginName, 0, $nextSlash);
@@ -133,11 +133,11 @@ class Theme
// Strip trailing query string
$fileToCheck = $overridingAsset;
$queryStringPos = strpos($fileToCheck, '?');
- if( $queryStringPos !== false) {
+ if ( $queryStringPos !== false) {
$fileToCheck = substr($fileToCheck, 0, $queryStringPos);
}
- if(file_exists($fileToCheck)) {
+ if (file_exists($fileToCheck)) {
return str_replace($pathAsset, $overridingAsset, $source);
}
return $source;
diff --git a/core/Tracker.php b/core/Tracker.php
index 733f23d8a3..3aa27237a1 100644
--- a/core/Tracker.php
+++ b/core/Tracker.php
@@ -221,7 +221,7 @@ class Tracker
*/
public function main($args = null)
{
- if(!SettingsPiwik::isPiwikInstalled()) {
+ if (!SettingsPiwik::isPiwikInstalled()) {
return $this->handleEmptyRequest();
}
try {
@@ -468,7 +468,7 @@ class Tracker
Common::sendHeader('Content-Type: text/html; charset=utf-8');
echo $this->getMessageFromException($e);
} else {
- $this->outputTransparentGif();
+ $this->outputTransparentGif ();
}
exit;
}
@@ -516,7 +516,7 @@ class Tracker
}
switch ($this->getState()) {
case self::STATE_LOGGING_DISABLE:
- $this->outputTransparentGif();
+ $this->outputTransparentGif ();
Common::printDebug("Logging disabled, display transparent logo");
break;
@@ -528,7 +528,7 @@ class Tracker
case self::STATE_NOSCRIPT_REQUEST:
case self::STATE_NOTHING_TO_NOTICE:
default:
- $this->outputTransparentGif();
+ $this->outputTransparentGif ();
Common::printDebug("Nothing to notice => default behaviour");
break;
}
@@ -663,7 +663,7 @@ class Tracker
return $visit;
}
- protected function outputTransparentGif()
+ protected function outputTransparentGif ()
{
if (isset($GLOBALS['PIWIK_TRACKER_DEBUG'])
&& $GLOBALS['PIWIK_TRACKER_DEBUG']
@@ -717,7 +717,7 @@ class Tracker
protected function handleEmptyRequest(Request $request = null)
{
- if(is_null($request)) {
+ if (is_null($request)) {
$request = new Request($_GET + $_POST);
}
$countParameters = $request->getParamsCount();
diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php
index 6175d86e59..6df6f64cfc 100644
--- a/core/Tracker/Action.php
+++ b/core/Tracker/Action.php
@@ -22,33 +22,51 @@ use Piwik\Tracker;
*/
abstract class Action
{
- const TYPE_PAGE_URL = 1;
- const TYPE_OUTLINK = 2;
- const TYPE_DOWNLOAD = 3;
+ const TYPE_PAGE_URL = 1;
+ const TYPE_OUTLINK = 2;
+ const TYPE_DOWNLOAD = 3;
const TYPE_PAGE_TITLE = 4;
- const TYPE_ECOMMERCE_ITEM_SKU = 5;
+ const TYPE_ECOMMERCE_ITEM_SKU = 5;
const TYPE_ECOMMERCE_ITEM_NAME = 6;
const TYPE_ECOMMERCE_ITEM_CATEGORY = 7;
const TYPE_SITE_SEARCH = 8;
- const TYPE_EVENT = 10; // Alias TYPE_EVENT_CATEGORY
+ const TYPE_EVENT = 10; // Alias TYPE_EVENT_CATEGORY
const TYPE_EVENT_CATEGORY = 10;
- const TYPE_EVENT_ACTION = 11;
- const TYPE_EVENT_NAME = 12;
+ const TYPE_EVENT_ACTION = 11;
+ const TYPE_EVENT_NAME = 12;
- const TYPE_CONTENT = 13; // Alias TYPE_CONTENT_NAME
- const TYPE_CONTENT_NAME = 13;
- const TYPE_CONTENT_PIECE = 14;
- const TYPE_CONTENT_TARGET = 15;
+ const TYPE_CONTENT = 13; // Alias TYPE_CONTENT_NAME
+ const TYPE_CONTENT_NAME = 13;
+ const TYPE_CONTENT_PIECE = 14;
+ const TYPE_CONTENT_TARGET = 15;
const TYPE_CONTENT_INTERACTION = 16;
const DB_COLUMN_CUSTOM_FLOAT = 'custom_float';
private static $factoryPriority = array(
- self::TYPE_PAGE_URL, self::TYPE_CONTENT, self::TYPE_SITE_SEARCH, self::TYPE_EVENT, self::TYPE_OUTLINK, self::TYPE_DOWNLOAD
+ self::TYPE_PAGE_URL,
+ self::TYPE_CONTENT,
+ self::TYPE_SITE_SEARCH,
+ self::TYPE_EVENT,
+ self::TYPE_OUTLINK,
+ self::TYPE_DOWNLOAD
);
/**
+ * Public so that events listener can access it
+ *
+ * @var Request
+ */
+ public $request;
+
+ private $idLinkVisitAction;
+ private $actionIdsCached = array();
+ private $actionName;
+ private $actionType;
+ private $actionUrl;
+
+ /**
* Makes the correct Action object based on the request.
*
* @param Request $request
@@ -102,7 +120,7 @@ abstract class Action
$instances = array();
foreach ($actions as $action) {
- /** @var \Piwik\Tracker\Action $instance */
+ /** @var \Piwik\Tracker\Action $action */
if ($action::shouldHandle($request)) {
$instances[] = new $action($request);
}
@@ -111,19 +129,6 @@ abstract class Action
return $instances;
}
- /**
- * Public so that events listener can access it
- *
- * @var Request
- */
- public $request;
-
- private $idLinkVisitAction;
- private $actionIdsCached = array();
- private $actionName;
- private $actionType;
- private $actionUrl;
-
public function __construct($type, Request $request)
{
$this->actionType = $type;
@@ -152,8 +157,7 @@ abstract class Action
public function getCustomVariables()
{
- $customVariables = $this->request->getCustomVariables($scope = 'page');
- return $customVariables;
+ return $this->request->getCustomVariables($scope = 'page');
}
// custom_float column
@@ -164,8 +168,7 @@ abstract class Action
protected function setActionName($name)
{
- $name = PageUrl::cleanupString((string)$name);
- $this->actionName = $name;
+ $this->actionName = PageUrl::cleanupString((string)$name);
}
protected function setActionUrl($url)
@@ -178,8 +181,7 @@ abstract class Action
Common::printDebug(' After is "' . $url . '"');
}
- $url = PageUrl::getUrlIfLookValid($url);
- $this->actionUrl = $url;
+ $this->actionUrl = PageUrl::getUrlIfLookValid($url);
}
abstract protected function getActionsToLookup();
@@ -187,11 +189,13 @@ abstract class Action
protected function getUrlAndType()
{
$url = $this->getActionUrl();
+
if (!empty($url)) {
// normalize urls by stripping protocol and www
$url = PageUrl::normalizeUrl($url);
return array($url['url'], self::TYPE_PAGE_URL, $url['prefixId']);
}
+
return false;
}
@@ -214,9 +218,11 @@ abstract class Action
public function getIdActionName()
{
- if(!isset($this->actionIdsCached['idaction_name'])) {
+ if (!isset($this->actionIdsCached['idaction_name'])) {
+
return false;
}
+
return $this->actionIdsCached['idaction_name'];
}
@@ -230,24 +236,17 @@ abstract class Action
return $this->idLinkVisitAction;
}
- public function writeDebugInfo()
- {
- $type = self::getTypeAsString($this->getActionType());
- Common::printDebug("Action is a $type,
- Action name = " . $this->getActionName() . ",
- Action URL = " . $this->getActionUrl());
- return true;
- }
-
public static function getTypeAsString($type)
{
- $class = new \ReflectionClass("\\Piwik\\Tracker\\Action");
+ $class = new \ReflectionClass("\\Piwik\\Tracker\\Action");
$constants = $class->getConstants();
$typeId = array_search($type, $constants);
- if($typeId === false) {
+
+ if (false === $typeId) {
throw new Exception("Unexpected action type " . $type);
}
+
return str_replace('TYPE_', '', $typeId);
}
@@ -262,24 +261,27 @@ abstract class Action
*/
public function loadIdsFromLogActionTable()
{
- if(!empty($this->actionIdsCached)) {
+ if (!empty($this->actionIdsCached)) {
return;
}
- $actions = $this->getActionsToLookup();
+ /** @var ActionDimension[] $dimensions */
$dimensions = ActionDimension::getAllDimensions();
+ $actions = $this->getActionsToLookup();
foreach ($dimensions as $dimension) {
$value = $dimension->onLookupAction($this->request, $this);
- if ($value !== false) {
+ if (false !== $value) {
$field = $dimension->getColumnName();
if (empty($field)) {
- throw new Exception('Dimension ' . get_class($dimension) . ' does not define a field name');
+ $dimensionClass = get_class($dimension);
+ throw new Exception('Dimension ' . $dimensionClass . ' does not define a field name');
}
- $actions[$field] = array($value, $dimension->getActionId());
+ $actionId = $dimension->getActionId();
+ $actions[$field] = array($value, $actionId);
Common::printDebug("$field = $value");
}
}
@@ -316,6 +318,7 @@ abstract class Action
'idaction_name_ref' => $idReferrerActionName
);
+ /** @var ActionDimension[] $dimensions */
$dimensions = ActionDimension::getAllDimensions();
foreach ($dimensions as $dimension) {
@@ -347,18 +350,15 @@ abstract class Action
}
$visitAction = array_merge($visitAction, $customVariables);
- $fields = implode(", ", array_keys($visitAction));
- $bind = array_values($visitAction);
- $values = Common::getSqlStringFieldsArray($visitAction);
- $sql = "INSERT INTO " . Common::prefixTable('log_link_visit_action') . " ($fields) VALUES ($values)";
- Tracker::getDatabase()->query($sql, $bind);
+ $this->idLinkVisitAction = $this->getModel()->createAction($visitAction);
- $this->idLinkVisitAction = Tracker::getDatabase()->lastInsertId();
$visitAction['idlink_va'] = $this->idLinkVisitAction;
Common::printDebug("Inserted new action:");
- Common::printDebug($visitAction);
+ $visitActionDebug = $visitAction;
+ $visitActionDebug['idvisitor'] = bin2hex($visitActionDebug['idvisitor']);
+ Common::printDebug($visitActionDebug);
/**
* Triggered after successfully persisting a [visit action entity](/guides/persistence-and-the-mysql-backend#visit-actions).
@@ -370,13 +370,31 @@ abstract class Action
Piwik::postEvent('Tracker.recordAction', array($trackerAction = $this, $visitAction));
}
+ public function writeDebugInfo()
+ {
+ $type = self::getTypeAsString($this->getActionType());
+ $name = $this->getActionName();
+ $url = $this->getActionUrl();
+
+ Common::printDebug("Action is a $type,
+ Action name = " . $name . ",
+ Action URL = " . $url);
+
+ return true;
+ }
+
+ private function getModel()
+ {
+ return new Model();
+ }
+
/**
* @return bool
*/
- protected function isActionHasActionName()
+ private function isActionHasActionName()
{
- return in_array($this->getActionType(), array(self::TYPE_PAGE_TITLE,
- self::TYPE_PAGE_URL,
- self::TYPE_SITE_SEARCH));
+ $types = array(self::TYPE_PAGE_TITLE, self::TYPE_PAGE_URL, self::TYPE_SITE_SEARCH);
+
+ return in_array($this->getActionType(), $types);
}
}
diff --git a/core/Tracker/ActionPageview.php b/core/Tracker/ActionPageview.php
index fa3d0146dc..9088fc543a 100644
--- a/core/Tracker/ActionPageview.php
+++ b/core/Tracker/ActionPageview.php
@@ -38,7 +38,7 @@ class ActionPageview extends Action
{
return array(
'idaction_name' => array($this->getActionName(), Action::TYPE_PAGE_TITLE),
- 'idaction_url' => $this->getUrlAndType()
+ 'idaction_url' => $this->getUrlAndType()
);
}
@@ -55,22 +55,38 @@ class ActionPageview extends Action
private function cleanupActionName($actionName)
{
// get the delimiter, by default '/'; BC, we read the old action_category_delimiter first (see #1067)
- $actionCategoryDelimiter = isset(Config::getInstance()->General['action_category_delimiter'])
- ? Config::getInstance()->General['action_category_delimiter']
- : Config::getInstance()->General['action_url_category_delimiter'];
+ $actionCategoryDelimiter = $this->getActionCategoryDelimiter();
// create an array of the categories delimited by the delimiter
$split = explode($actionCategoryDelimiter, $actionName);
+ $split = $this->trimEveryCategory($split);
+ $split = $this->removeEmptyCategories($split);
- // trim every category
- $split = array_map('trim', $split);
+ return $this->rebuildNameOfCleanedCategories($actionCategoryDelimiter, $split);
+ }
+
+ private function rebuildNameOfCleanedCategories($actionCategoryDelimiter, $split)
+ {
+ return implode($actionCategoryDelimiter, $split);
+ }
- // remove empty categories
- $split = array_filter($split, 'strlen');
+ private function removeEmptyCategories($split)
+ {
+ return array_filter($split, 'strlen');
+ }
+
+ private function trimEveryCategory($split)
+ {
+ return array_map('trim', $split);
+ }
+
+ private function getActionCategoryDelimiter()
+ {
+ if (isset(Config::getInstance()->General['action_category_delimiter'])) {
+ return Config::getInstance()->General['action_category_delimiter'];
+ }
- // rebuild the name from the array of cleaned categories
- $actionName = implode($actionCategoryDelimiter, $split);
- return $actionName;
+ return Config::getInstance()->General['action_url_category_delimiter'];
}
}
diff --git a/core/Tracker/Cache.php b/core/Tracker/Cache.php
index 0217b175de..b8f0413c63 100644
--- a/core/Tracker/Cache.php
+++ b/core/Tracker/Cache.php
@@ -8,6 +8,7 @@
*/
namespace Piwik\Tracker;
+use Piwik\Access;
use Piwik\ArchiveProcessor\Rules;
use Piwik\CacheFile;
use Piwik\Common;
@@ -45,56 +46,54 @@ class Cache
*/
static function getCacheWebsiteAttributes($idSite)
{
- if($idSite == 'all') {
+ if ('all' == $idSite) {
return array();
}
+
$idSite = (int)$idSite;
- if($idSite <= 0) {
+ if ($idSite <= 0) {
return array();
}
- $cache = self::getInstance();
- if (($cacheContent = $cache->get($idSite)) !== false) {
+ $cache = self::getInstance();
+ $cacheContent = $cache->get($idSite);
+
+ if (false !== $cacheContent) {
return $cacheContent;
}
Tracker::initCorePiwikInTrackerMode();
- // save current user privilege and temporarily assume Super User privilege
- $isSuperUser = Piwik::hasUserSuperUserAccess();
- Piwik::setUserHasSuperUserAccess();
-
$content = array();
-
- /**
- * Triggered to get the attributes of a site entity that might be used by the
- * Tracker.
- *
- * Plugins add new site attributes for use in other tracking events must
- * use this event to put those attributes in the Tracker Cache.
- *
- * **Example**
- *
- * public function getSiteAttributes($content, $idSite)
- * {
- * $sql = "SELECT info FROM " . Common::prefixTable('myplugin_extra_site_info') . " WHERE idsite = ?";
- * $content['myplugin_site_data'] = Db::fetchOne($sql, array($idSite));
- * }
- *
- * @param array &$content Array mapping of site attribute names with values.
- * @param int $idSite The site ID to get attributes for.
- */
- Piwik::postEvent('Tracker.Cache.getSiteAttributes', array(&$content, $idSite));
- Common::printDebug("Website $idSite tracker cache was re-created.");
-
- // restore original user privilege
- Piwik::setUserHasSuperUserAccess($isSuperUser);
+ Access::doAsSuperUser(function () use (&$content, $idSite) {
+ /**
+ * Triggered to get the attributes of a site entity that might be used by the
+ * Tracker.
+ *
+ * Plugins add new site attributes for use in other tracking events must
+ * use this event to put those attributes in the Tracker Cache.
+ *
+ * **Example**
+ *
+ * public function getSiteAttributes($content, $idSite)
+ * {
+ * $sql = "SELECT info FROM " . Common::prefixTable('myplugin_extra_site_info') . " WHERE idsite = ?";
+ * $content['myplugin_site_data'] = Db::fetchOne($sql, array($idSite));
+ * }
+ *
+ * @param array &$content Array mapping of site attribute names with values.
+ * @param int $idSite The site ID to get attributes for.
+ */
+ Piwik::postEvent('Tracker.Cache.getSiteAttributes', array(&$content, $idSite));
+ Common::printDebug("Website $idSite tracker cache was re-created.");
+ });
// if nothing is returned from the plugins, we don't save the content
// this is not expected: all websites are expected to have at least one URL
if (!empty($content)) {
$cache->set($idSite, $content);
}
+
return $content;
}
@@ -114,10 +113,12 @@ class Cache
*/
public static function getCacheGeneral()
{
- $cache = self::getInstance();
+ $cache = self::getInstance();
$cacheId = 'general';
- if (($cacheContent = $cache->get($cacheId)) !== false) {
+ $cacheContent = $cache->get($cacheId);
+
+ if (false !== $cacheContent) {
return $cacheContent;
}
@@ -161,9 +162,10 @@ class Cache
*/
public static function setCacheGeneral($value)
{
- $cache = self::getInstance();
+ $cache = self::getInstance();
$cacheId = 'general';
$cache->set($cacheId, $value);
+
return true;
}
@@ -177,6 +179,7 @@ class Cache
if (!is_array($idSites)) {
$idSites = array($idSites);
}
+
foreach ($idSites as $idSite) {
self::deleteCacheWebsiteAttributes($idSite);
self::getCacheWebsiteAttributes($idSite);
diff --git a/core/Tracker/Db.php b/core/Tracker/Db.php
index 4ce13a0891..1fe081daac 100644
--- a/core/Tracker/Db.php
+++ b/core/Tracker/Db.php
@@ -77,10 +77,14 @@ abstract class Db
*/
protected function recordQueryProfile($query, $timer)
{
- if (!isset($this->queriesProfiling[$query])) $this->queriesProfiling[$query] = array('sum_time_ms' => 0, 'count' => 0);
- $time = $timer->getTimeMs(2);
+ if (!isset($this->queriesProfiling[$query])) {
+ $this->queriesProfiling[$query] = array('sum_time_ms' => 0, 'count' => 0);
+ }
+
+ $time = $timer->getTimeMs(2);
$time += $this->queriesProfiling[$query]['sum_time_ms'];
$count = $this->queriesProfiling[$query]['count'] + 1;
+
$this->queriesProfiling[$query] = array('sum_time_ms' => $time, 'count' => $count);
}
@@ -97,13 +101,12 @@ abstract class Db
self::$profiling = false;
foreach ($this->queriesProfiling as $query => $info) {
- $time = $info['sum_time_ms'];
+ $time = $info['sum_time_ms'];
$count = $info['count'];
$queryProfiling = "INSERT INTO " . Common::prefixTable('log_profiling') . "
(query,count,sum_time_ms) VALUES (?,$count,$time)
- ON DUPLICATE KEY
- UPDATE count=count+$count,sum_time_ms=sum_time_ms+$time";
+ ON DUPLICATE KEY UPDATE count=count+$count,sum_time_ms=sum_time_ms+$time";
$this->query($queryProfiling, array($query));
}
diff --git a/core/Tracker/Db/Mysqli.php b/core/Tracker/Db/Mysqli.php
index 78e2d9ee29..9452551683 100644
--- a/core/Tracker/Db/Mysqli.php
+++ b/core/Tracker/Db/Mysqli.php
@@ -73,7 +73,14 @@ class Mysqli extends Db
$timer = $this->initProfiler();
}
- $this->connection = mysqli_connect($this->host, $this->username, $this->password, $this->dbname, $this->port, $this->socket);
+ $this->connection = mysqli_init();
+
+ // Make sure MySQL returns all matched rows on update queries including
+ // rows that actually didn't have to be updated because the values didn't
+ // change. This matches common behaviour among other database systems.
+ // See #6296 why this is important in tracker
+ $flags = MYSQLI_CLIENT_FOUND_ROWS;
+ mysqli_real_connect($this->connection, $this->host, $this->username, $this->password, $this->dbname, $this->port, $this->socket, $flags);
if (!$this->connection || mysqli_connect_errno()) {
throw new DbException("Connect failed: " . mysqli_connect_error());
}
@@ -205,8 +212,8 @@ class Mysqli extends Db
return $result;
} catch (Exception $e) {
throw new DbException("Error query: " . $e->getMessage() . "
- In query: $query
- Parameters: " . var_export($parameters, true));
+ In query: $query
+ Parameters: " . var_export($parameters, true));
}
}
@@ -278,57 +285,58 @@ class Mysqli extends Db
return mysqli_affected_rows($this->connection);
}
- /**
- * Start Transaction
- * @return string TransactionID
- */
-
- public function beginTransaction()
- {
- if(!$this->activeTransaction === false ) {
- return;
- }
-
- if( $this->connection->autocommit(false) ) {
- $this->activeTransaction = uniqid();
- return $this->activeTransaction;
- }
- }
-
- /**
- * Commit Transaction
- * @param string TransactionID from beginTransaction
- */
-
- public function commit($xid)
- {
- if($this->activeTransaction != $xid || $this->activeTransaction === false ) {
-
- return;
- }
- $this->activeTransaction = false;
-
- if(!$this->connection->commit() ) {
- throw new DbException("Commit failed");
- }
- $this->connection->autocommit(true);
- }
-
- /**
- * Rollback Transaction
- * @param string TransactionID from beginTransaction
- */
-
- public function rollBack($xid)
- {
- if($this->activeTransaction != $xid || $this->activeTransaction === false ) {
- return;
- }
- $this->activeTransaction = false;
-
- if(!$this->connection->rollback() ) {
- throw new DbException("Rollback failed");
- }
- $this->connection->autocommit(true);
- }
+ /**
+ * Start Transaction
+ * @return string TransactionID
+ */
+ public function beginTransaction()
+ {
+ if (!$this->activeTransaction === false ) {
+ return;
+ }
+
+ if ( $this->connection->autocommit(false) ) {
+ $this->activeTransaction = uniqid();
+ return $this->activeTransaction;
+ }
+ }
+
+ /**
+ * Commit Transaction
+ * @param $xid
+ * @throws DbException
+ * @internal param TransactionID $string from beginTransaction
+ */
+ public function commit($xid)
+ {
+ if ($this->activeTransaction != $xid || $this->activeTransaction === false ) {
+
+ return;
+ }
+ $this->activeTransaction = false;
+
+ if (!$this->connection->commit() ) {
+ throw new DbException("Commit failed");
+ }
+ $this->connection->autocommit(true);
+ }
+
+ /**
+ * Rollback Transaction
+ * @param $xid
+ * @throws DbException
+ * @internal param TransactionID $string from beginTransaction
+ */
+ public function rollBack($xid)
+ {
+ if ($this->activeTransaction != $xid || $this->activeTransaction === false ) {
+ return;
+ }
+ $this->activeTransaction = false;
+
+ if (!$this->connection->rollback() ) {
+ throw new DbException("Rollback failed");
+ }
+ $this->connection->autocommit(true);
+ }
}
diff --git a/core/Tracker/Db/Pdo/Mysql.php b/core/Tracker/Db/Pdo/Mysql.php
index a2f5a79b2c..1cb72c11a6 100644
--- a/core/Tracker/Db/Pdo/Mysql.php
+++ b/core/Tracker/Db/Pdo/Mysql.php
@@ -68,8 +68,17 @@ class Mysql extends Db
$timer = $this->initProfiler();
}
- $this->connection = @new PDO($this->dsn, $this->username, $this->password, $config = array());
- $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ // Make sure MySQL returns all matched rows on update queries including
+ // rows that actually didn't have to be updated because the values didn't
+ // change. This matches common behaviour among other database systems.
+ // See #6296 why this is important in tracker
+ $config = array(
+ PDO::MYSQL_ATTR_FOUND_ROWS => true,
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ );
+
+ $this->connection = @new PDO($this->dsn, $this->username, $this->password, $config);
+
// we may want to setAttribute(PDO::ATTR_TIMEOUT ) to a few seconds (default is 60) in case the DB is locked
// the piwik.php would stay waiting for the database... bad!
// we delete the password from this object "just in case" it could be printed
@@ -195,8 +204,8 @@ class Mysql extends Db
return $sth;
} catch (PDOException $e) {
throw new DbException("Error query: " . $e->getMessage() . "
- In query: $query
- Parameters: " . var_export($parameters, true));
+ In query: $query
+ Parameters: " . var_export($parameters, true));
}
}
@@ -237,54 +246,55 @@ class Mysql extends Db
return $queryResult->rowCount();
}
- /**
- * Start Transaction
- * @return string TransactionID
- */
-
- public function beginTransaction()
- {
- if(!$this->activeTransaction === false ) {
- return;
- }
-
- if( $this->connection->beginTransaction() ) {
- $this->activeTransaction = uniqid();
- return $this->activeTransaction;
- }
- }
-
- /**
- * Commit Transaction
- * @param string TransactionID from beginTransaction
- */
+ /**
+ * Start Transaction
+ * @return string TransactionID
+ */
+ public function beginTransaction()
+ {
+ if (!$this->activeTransaction === false ) {
+ return;
+ }
- public function commit($xid)
- {
- if($this->activeTransaction != $xid || $this->activeTransaction === false ) {
- return;
- }
- $this->activeTransaction = false;
+ if ( $this->connection->beginTransaction() ) {
+ $this->activeTransaction = uniqid();
+ return $this->activeTransaction;
+ }
+ }
- if(!$this->connection->commit() ) {
- throw new DbException("Commit failed");
- }
- }
+ /**
+ * Commit Transaction
+ * @param $xid
+ * @throws DbException
+ * @internal param TransactionID $string from beginTransaction
+ */
+ public function commit($xid)
+ {
+ if ($this->activeTransaction != $xid || $this->activeTransaction === false ) {
+ return;
+ }
+ $this->activeTransaction = false;
- /**
- * Rollback Transaction
- * @param string TransactionID from beginTransaction
- */
+ if (!$this->connection->commit() ) {
+ throw new DbException("Commit failed");
+ }
+ }
- public function rollBack($xid)
- {
- if($this->activeTransaction != $xid || $this->activeTransaction === false ) {
- return;
- }
- $this->activeTransaction = false;
+ /**
+ * Rollback Transaction
+ * @param $xid
+ * @throws DbException
+ * @internal param TransactionID $string from beginTransaction
+ */
+ public function rollBack($xid)
+ {
+ if ($this->activeTransaction != $xid || $this->activeTransaction === false ) {
+ return;
+ }
+ $this->activeTransaction = false;
- if(!$this->connection->rollBack() ) {
- throw new DbException("Rollback failed");
- }
- }
+ if (!$this->connection->rollBack() ) {
+ throw new DbException("Rollback failed");
+ }
+ }
}
diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php
index 1a0944ad9a..69639fd1c6 100644
--- a/core/Tracker/GoalManager.php
+++ b/core/Tracker/GoalManager.php
@@ -34,6 +34,25 @@ class GoalManager
const REVENUE_PRECISION = 2;
const MAXIMUM_PRODUCT_CATEGORIES = 5;
+
+ // In the GET items parameter, each item has the following array of information
+ const INDEX_ITEM_SKU = 0;
+ const INDEX_ITEM_NAME = 1;
+ const INDEX_ITEM_CATEGORY = 2;
+ const INDEX_ITEM_PRICE = 3;
+ const INDEX_ITEM_QUANTITY = 4;
+
+ // Used in the array of items, internally to this class
+ const INTERNAL_ITEM_SKU = 0;
+ const INTERNAL_ITEM_NAME = 1;
+ const INTERNAL_ITEM_CATEGORY = 2;
+ const INTERNAL_ITEM_CATEGORY2 = 3;
+ const INTERNAL_ITEM_CATEGORY3 = 4;
+ const INTERNAL_ITEM_CATEGORY4 = 5;
+ const INTERNAL_ITEM_CATEGORY5 = 6;
+ const INTERNAL_ITEM_PRICE = 7;
+ const INTERNAL_ITEM_QUANTITY = 8;
+
public $idGoal;
public $requestIsEcommerce;
private $isGoalAnOrder;
@@ -65,7 +84,7 @@ class GoalManager
$this->idGoal = $request->getParam('idgoal');
$this->isGoalAnOrder = !empty($this->orderId);
- $this->requestIsEcommerce = ($this->idGoal == 0);
+ $this->requestIsEcommerce = (0 == $this->idGoal);
}
public function isGoalAnOrder()
@@ -87,30 +106,36 @@ class GoalManager
public static function getGoalDefinitions($idSite)
{
$websiteAttributes = Cache::getCacheWebsiteAttributes($idSite);
+
if (isset($websiteAttributes['goals'])) {
return $websiteAttributes['goals'];
}
+
return array();
}
public static function getGoalDefinition($idSite, $idGoal)
{
$goals = self::getGoalDefinitions($idSite);
+
foreach ($goals as $goal) {
if ($goal['idgoal'] == $idGoal) {
return $goal;
}
}
+
throw new Exception('Goal not found');
}
public static function getGoalIds($idSite)
{
- $goals = self::getGoalDefinitions($idSite);
+ $goals = self::getGoalDefinitions($idSite);
$goalIds = array();
+
foreach ($goals as $goal) {
$goalIds[] = $goal['idgoal'];
}
+
return $goalIds;
}
@@ -131,14 +156,15 @@ class GoalManager
$decodedActionUrl = $action->getActionUrl();
$actionType = $action->getActionType();
$goals = $this->getGoalDefinitions($idSite);
+
foreach ($goals as $goal) {
$attribute = $goal['match_attribute'];
// if the attribute to match is not the type of the current action
- if ( (($attribute == 'url' || $attribute == 'title') && $actionType != Action::TYPE_PAGE_URL)
- || ($attribute == 'file' && $actionType != Action::TYPE_DOWNLOAD)
- || ($attribute == 'external_website' && $actionType != Action::TYPE_OUTLINK)
- || ($attribute == 'manually')
- || in_array($attribute, array('event_action', 'event_name', 'event_category')) && $actionType != Action::TYPE_EVENT
+ if ((($attribute == 'url' || $attribute == 'title') && $actionType != Action::TYPE_PAGE_URL)
+ || ($attribute == 'file' && $actionType != Action::TYPE_DOWNLOAD)
+ || ($attribute == 'external_website' && $actionType != Action::TYPE_OUTLINK)
+ || ($attribute == 'manually')
+ || in_array($attribute, array('event_action', 'event_name', 'event_category')) && $actionType != Action::TYPE_EVENT
) {
continue;
}
@@ -169,23 +195,33 @@ class GoalManager
$this->convertedGoals[] = $goal;
}
}
+
return count($this->convertedGoals) > 0;
}
+ public function isManualGoalConversion()
+ {
+ return $this->idGoal > 0;
+ }
+
public function detectGoalId($idSite)
{
if (!Common::isGoalPluginEnabled()) {
return false;
}
+
$goals = $this->getGoalDefinitions($idSite);
+
if (!isset($goals[$this->idGoal])) {
return false;
}
+
$goal = $goals[$this->idGoal];
- $url = $this->request->getParam('url');
+ $url = $this->request->getParam('url');
$goal['url'] = PageUrl::excludeQueryParametersFromUrl($url, $idSite);
$this->convertedGoals[] = $goal;
+
return true;
}
@@ -199,18 +235,7 @@ class GoalManager
*/
public function recordGoals(Visitor $visitor, $visitorInformation, $visitCustomVariables, $action)
{
- $goal = array(
- 'idvisit' => $visitorInformation['idvisit'],
- 'idvisitor' => $visitorInformation['idvisitor'],
- 'server_time' => Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time'])
- );
-
- foreach (VisitDimension::getAllDimensions() as $dimension) {
- $value = $dimension->onAnyGoalConversion($this->request, $visitor, $action);
- if (false !== $value) {
- $goal[$dimension->getColumnName()] = $value;
- }
- }
+ $goal = $this->getGoalFromVisitor($visitor, $visitorInformation, $action);
// Copy Custom Variables from Visit row to the Goal conversion
// Otherwise, set the Custom Variables found in the cookie sent with this request
@@ -249,6 +274,7 @@ class GoalManager
if (round($revenue) == $revenue) {
return $revenue;
}
+
return round($revenue, self::REVENUE_PRECISION);
}
@@ -291,7 +317,8 @@ class GoalManager
// INSERT or Sync items in the Cart / Order for this visit & order
$items = $this->getEcommerceItemsFromRequest();
- if ($items === false) {
+
+ if (false === $items) {
return;
}
@@ -303,12 +330,7 @@ class GoalManager
$conversion['items'] = $itemsCount;
if ($this->isThereExistingCartInVisit) {
- $updateWhere = array(
- 'idvisit' => $visitInformation['idvisit'],
- 'idgoal' => self::IDGOAL_CART,
- 'buster' => 0,
- );
- $recorded = $this->updateExistingConversion($conversion, $updateWhere);
+ $recorded = $this->getModel()->updateConversion($visitInformation['idvisit'], self::IDGOAL_CART, $conversion);
} else {
$recorded = $this->insertNewConversion($conversion, $visitInformation);
}
@@ -338,12 +360,15 @@ class GoalManager
private function getEcommerceItemsFromRequest()
{
$items = Common::unsanitizeInputValue($this->request->getParam('ec_items'));
+
if (empty($items)) {
Common::printDebug("There are no Ecommerce items in the request");
// we still record an Ecommerce order without any item in it
return array();
}
+
$items = Common::json_decode($items, $assoc = true);
+
if (!is_array($items)) {
Common::printDebug("Error while json_decode the Ecommerce items = " . var_export($items, true));
return false;
@@ -368,23 +393,11 @@ class GoalManager
$itemInCartBySku[$item[0]] = $item;
}
- // Select all items currently in the Cart if any
- $sql = "SELECT idaction_sku, idaction_name, idaction_category, idaction_category2, idaction_category3, idaction_category4, idaction_category5, price, quantity, deleted, idorder as idorder_original_value
- FROM " . Common::prefixTable('log_conversion_item') . "
- WHERE idvisit = ?
- AND (idorder = ? OR idorder = ?)";
+ $itemsInDb = $this->getModel()->getAllItemsCurrentlyInTheCart($goal, self::ITEM_IDORDER_ABANDONED_CART);
- $bind = array($goal['idvisit'],
- isset($goal['idorder']) ? $goal['idorder'] : self::ITEM_IDORDER_ABANDONED_CART,
- self::ITEM_IDORDER_ABANDONED_CART
- );
-
- $itemsInDb = Tracker::getDatabase()->fetchAll($sql, $bind);
-
- Common::printDebug("Items found in current cart, for conversion_item (visit,idorder)=" . var_export($bind, true));
- Common::printDebug($itemsInDb);
// Look at which items need to be deleted, which need to be added or updated, based on the SKU
$skuFoundInDb = $itemsToUpdate = array();
+
foreach ($itemsInDb as $itemInDb) {
$skuFoundInDb[] = $itemInDb['idaction_sku'];
@@ -435,27 +448,10 @@ class GoalManager
$itemsToInsert[] = $item;
}
}
+
$this->insertEcommerceItems($goal, $itemsToInsert);
}
- // In the GET items parameter, each item has the following array of information
- const INDEX_ITEM_SKU = 0;
- const INDEX_ITEM_NAME = 1;
- const INDEX_ITEM_CATEGORY = 2;
- const INDEX_ITEM_PRICE = 3;
- const INDEX_ITEM_QUANTITY = 4;
-
- // Used in the array of items, internally to this class
- const INTERNAL_ITEM_SKU = 0;
- const INTERNAL_ITEM_NAME = 1;
- const INTERNAL_ITEM_CATEGORY = 2;
- const INTERNAL_ITEM_CATEGORY2 = 3;
- const INTERNAL_ITEM_CATEGORY3 = 4;
- const INTERNAL_ITEM_CATEGORY4 = 5;
- const INTERNAL_ITEM_CATEGORY5 = 6;
- const INTERNAL_ITEM_PRICE = 7;
- const INTERNAL_ITEM_QUANTITY = 8;
-
/**
* Reads items from the request, then looks up the names from the lookup table
* and returns a clean array of items ready for the database.
@@ -468,9 +464,10 @@ class GoalManager
// Clean up the items array
$cleanedItems = array();
foreach ($items as $item) {
- $name = $category = $category2 = $category3 = $category4 = $category5 = false;
- $price = 0;
+ $name = $category = $category2 = $category3 = $category4 = $category5 = false;
+ $price = 0;
$quantity = 1;
+
// items are passed in the request as an array: ( $sku, $name, $category, $price, $quantity )
if (empty($item[self::INDEX_ITEM_SKU])) {
continue;
@@ -562,6 +559,7 @@ class GoalManager
$item[5] = $actionsLookedUp[$index * $columnsInEachRow + 5];
$item[6] = $actionsLookedUp[$index * $columnsInEachRow + 6];
}
+
return $cleanedItems;
}
@@ -579,29 +577,23 @@ class GoalManager
if (empty($itemsToUpdate)) {
return;
}
+
Common::printDebug("Goal data used to update ecommerce items:");
Common::printDebug($goal);
foreach ($itemsToUpdate as $item) {
$newRow = $this->getItemRowEnriched($goal, $item);
Common::printDebug($newRow);
- $updateParts = $sqlBind = array();
- foreach ($newRow as $name => $value) {
- $updateParts[] = $name . " = ?";
- $sqlBind[] = $value;
- }
- $sql = 'UPDATE ' . Common::prefixTable('log_conversion_item') . "
- SET " . implode($updateParts, ', ') . "
- WHERE idvisit = ?
- AND idorder = ?
- AND idaction_sku = ?";
- $sqlBind[] = $newRow['idvisit'];
- $sqlBind[] = $item['idorder_original_value'];
- $sqlBind[] = $newRow['idaction_sku'];
- Tracker::getDatabase()->query($sql, $sqlBind);
+
+ $this->getModel()->updateEcommerceItem($item['idorder_original_value'], $newRow);
}
}
+ private function getModel()
+ {
+ return new Model();
+ }
+
/**
* Inserts in the cart in the DB the new items
* that were not previously in the cart
@@ -616,27 +608,17 @@ class GoalManager
if (empty($itemsToInsert)) {
return;
}
+
Common::printDebug("Ecommerce items that are added to the cart/order");
Common::printDebug($itemsToInsert);
- $sql = "INSERT INTO " . Common::prefixTable('log_conversion_item') . "
- (idaction_sku, idaction_name, idaction_category, idaction_category2, idaction_category3, idaction_category4, idaction_category5, price, quantity, deleted,
- idorder, idsite, idvisitor, server_time, idvisit)
- VALUES ";
- $i = 0;
- $bind = array();
+ $items = array();
+
foreach ($itemsToInsert as $item) {
- if ($i > 0) {
- $sql .= ',';
- }
- $newRow = array_values($this->getItemRowEnriched($goal, $item));
- $sql .= " ( " . Common::getSqlStringFieldsArray($newRow) . " ) ";
- $i++;
- $bind = array_merge($bind, $newRow);
- }
- Tracker::getDatabase()->query($sql, $bind);
- Common::printDebug($sql);
- Common::printDebug($bind);
+ $items[] = $this->getItemRowEnriched($goal, $item);
+ }
+
+ $this->getModel()->createEcommerceItems($items);
}
protected function getItemRowEnriched($goal, $item)
@@ -685,7 +667,7 @@ class GoalManager
Common::printDebug("- Goal " . $convertedGoal['idgoal'] . " matched. Recording...");
$conversion = $goal;
$conversion['idgoal'] = $convertedGoal['idgoal'];
- $conversion['url'] = $convertedGoal['url'];
+ $conversion['url'] = $convertedGoal['url'];
if (!is_null($action)) {
$conversion['idaction_url'] = $action->getIdActionUrl();
@@ -741,15 +723,9 @@ class GoalManager
$newGoalDebug['idvisitor'] = bin2hex($newGoalDebug['idvisitor']);
Common::printDebug($newGoalDebug);
- $fields = implode(", ", array_keys($conversion));
- $bindFields = Common::getSqlStringFieldsArray($conversion);
- $sql = 'INSERT IGNORE INTO ' . Common::prefixTable('log_conversion') . "
- ($fields) VALUES ($bindFields) ";
- $bind = array_values($conversion);
- $result = Tracker::getDatabase()->query($sql, $bind);
+ $wasInserted = $this->getModel()->createConversion($conversion);
- // If a record was inserted, we return true
- return Tracker::getDatabase()->rowCount($result) > 0;
+ return $wasInserted;
}
/**
@@ -772,30 +748,6 @@ class GoalManager
);
}
- protected function updateExistingConversion($newGoal, $updateWhere)
- {
- $updateParts = $sqlBind = $updateWhereParts = array();
- foreach ($newGoal as $name => $value) {
- $updateParts[] = $name . " = ?";
- $sqlBind[] = $value;
- }
- foreach ($updateWhere as $name => $value) {
- $updateWhereParts[] = $name . " = ?";
- $sqlBind[] = $value;
- }
- $sql = 'UPDATE ' . Common::prefixTable('log_conversion') . "
- SET " . implode($updateParts, ', ') . "
- WHERE " . implode($updateWhereParts, ' AND ');
-
- try {
- Tracker::getDatabase()->query($sql, $sqlBind);
- } catch(Exception $e){
- Common::printDebug("There was an error while updating the Conversion: " . $e->getMessage());
- return false;
- }
- return true;
- }
-
/**
* @param $goal
* @param $pattern_type
@@ -842,6 +794,7 @@ class GoalManager
throw new Exception(Piwik::translate('General_ExceptionInvalidGoalPattern', array($pattern_type)));
break;
}
+
return $match;
}
@@ -859,7 +812,7 @@ class GoalManager
foreach ($dimensions as $dimension) {
$value = $dimension->$hook($this->request, $visitor, $action, $this);
- if ($value !== false) {
+ if (false !== $value) {
$fieldName = $dimension->getColumnName();
$visitor->setVisitorColumn($fieldName, $value);
@@ -869,4 +822,24 @@ class GoalManager
return $valuesToUpdate;
}
+
+ private function getGoalFromVisitor(Visitor $visitor, $visitorInformation, $action)
+ {
+ $goal = array(
+ 'idvisit' => $visitorInformation['idvisit'],
+ 'idvisitor' => $visitorInformation['idvisitor'],
+ 'server_time' => Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time'])
+ );
+
+ $visitDimensions = VisitDimension::getAllDimensions();
+
+ foreach ($visitDimensions as $dimension) {
+ $value = $dimension->onAnyGoalConversion($this->request, $visitor, $action);
+ if (false !== $value) {
+ $goal[$dimension->getColumnName()] = $value;
+ }
+ }
+
+ return $goal;
+ }
}
diff --git a/core/Tracker/Model.php b/core/Tracker/Model.php
new file mode 100644
index 0000000000..090cd8be59
--- /dev/null
+++ b/core/Tracker/Model.php
@@ -0,0 +1,383 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Tracker;
+
+use Exception;
+use PDOStatement;
+use Piwik\Common;
+use Piwik\Tracker;
+use Piwik\Tracker\Db\DbException;
+
+class Model
+{
+
+ public function createAction($visitAction)
+ {
+ $fields = implode(", ", array_keys($visitAction));
+ $values = Common::getSqlStringFieldsArray($visitAction);
+ $table = Common::prefixTable('log_link_visit_action');
+
+ $sql = "INSERT INTO $table ($fields) VALUES ($values)";
+ $bind = array_values($visitAction);
+
+ $db = $this->getDb();
+ $db->query($sql, $bind);
+
+ $id = $db->lastInsertId();
+
+ return $id;
+ }
+
+ public function createConversion($conversion)
+ {
+ $fields = implode(", ", array_keys($conversion));
+ $bindFields = Common::getSqlStringFieldsArray($conversion);
+ $table = Common::prefixTable('log_conversion');
+
+ $sql = "INSERT IGNORE INTO $table ($fields) VALUES ($bindFields) ";
+ $bind = array_values($conversion);
+
+ $db = $this->getDb();
+ $result = $db->query($sql, $bind);
+
+ // If a record was inserted, we return true
+ return $db->rowCount($result) > 0;
+ }
+
+ public function updateConversion($idVisit, $idGoal, $newConversion)
+ {
+ $updateWhere = array(
+ 'idvisit' => $idVisit,
+ 'idgoal' => $idGoal,
+ 'buster' => 0,
+ );
+
+ $updateParts = $sqlBind = $updateWhereParts = array();
+
+ foreach ($newConversion as $name => $value) {
+ $updateParts[] = $name . " = ?";
+ $sqlBind[] = $value;
+ }
+
+ foreach ($updateWhere as $name => $value) {
+ $updateWhereParts[] = $name . " = ?";
+ $sqlBind[] = $value;
+ }
+
+ $parts = implode($updateParts, ', ');
+ $table = Common::prefixTable('log_conversion');
+
+ $sql = "UPDATE $table SET $parts WHERE " . implode($updateWhereParts, ' AND ');
+
+ try {
+ $this->getDb()->query($sql, $sqlBind);
+ } catch(Exception $e){
+ Common::printDebug("There was an error while updating the Conversion: " . $e->getMessage());
+
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Loads the Ecommerce items from the request and records them in the DB
+ *
+ * @param array $goal
+ * @param int $defaultIdOrder
+ * @throws Exception
+ * @return array
+ */
+ public function getAllItemsCurrentlyInTheCart($goal, $defaultIdOrder)
+ {
+ $sql = "SELECT idaction_sku, idaction_name, idaction_category, idaction_category2, idaction_category3, idaction_category4, idaction_category5, price, quantity, deleted, idorder as idorder_original_value
+ FROM " . Common::prefixTable('log_conversion_item') . "
+ WHERE idvisit = ? AND (idorder = ? OR idorder = ?)";
+
+ $bind = array(
+ $goal['idvisit'],
+ isset($goal['idorder']) ? $goal['idorder'] : $defaultIdOrder,
+ $defaultIdOrder
+ );
+
+ $itemsInDb = $this->getDb()->fetchAll($sql, $bind);
+
+ Common::printDebug("Items found in current cart, for conversion_item (visit,idorder)=" . var_export($bind, true));
+ Common::printDebug($itemsInDb);
+
+ return $itemsInDb;
+ }
+
+ public function createEcommerceItems($ecommerceItems)
+ {
+ $sql = "INSERT INTO " . Common::prefixTable('log_conversion_item');
+ $i = 0;
+ $bind = array();
+
+ foreach ($ecommerceItems as $item) {
+
+ if ($i === 0) {
+ $fields = implode(', ', array_keys($item));
+ $sql .= ' (' . $fields . ') VALUES ';
+ } elseif ($i > 0) {
+ $sql .= ',';
+ }
+
+ $newRow = array_values($item);
+ $sql .= " ( " . Common::getSqlStringFieldsArray($newRow) . " ) ";
+ $bind = array_merge($bind, $newRow);
+ $i++;
+ }
+
+ $this->getDb()->query($sql, $bind);
+
+ Common::printDebug($sql);
+ Common::printDebug($bind);
+ }
+
+ public function createNewIdAction($name, $type, $urlPrefix)
+ {
+ $table = Common::prefixTable('log_action');
+ $sql = "INSERT INTO $table (name, hash, type, url_prefix) VALUES (?,CRC32(?),?,?)";
+
+ $db = $this->getDb();
+ $db->query($sql, array($name, $name, $type, $urlPrefix));
+
+ $actionId = $db->lastInsertId();
+
+ return $actionId;
+ }
+
+ private function getSqlSelectActionId()
+ {
+ $sql = "SELECT idaction, type, name FROM " . Common::prefixTable('log_action')
+ . " WHERE ( hash = CRC32(?) AND name = ? AND type = ? ) ";
+
+ return $sql;
+ }
+
+ public function getIdActionMatchingNameAndType($name, $type)
+ {
+ $sql = $this->getSqlSelectActionId();
+ $bind = array($name, $name, $type);
+
+ $idAction = $this->getDb()->fetchOne($sql, $bind);
+
+ return $idAction;
+ }
+
+ public function getIdsAction($actionsNameAndType)
+ {
+ $sql = $this->getSqlSelectActionId();
+ $bind = array();
+
+ $i = 0;
+ foreach ($actionsNameAndType as $actionNameType) {
+ $name = $actionNameType['name'];
+
+ if (empty($name)) {
+ continue;
+ }
+
+ if ($i > 0) {
+ $sql .= " OR ( hash = CRC32(?) AND name = ? AND type = ? ) ";
+ }
+
+ $bind[] = $name;
+ $bind[] = $name;
+ $bind[] = $actionNameType['type'];
+ $i++;
+ }
+
+ // Case URL & Title are empty
+ if (empty($bind)) {
+ return false;
+ }
+
+ $actionIds = $this->getDb()->fetchAll($sql, $bind);
+
+ return $actionIds;
+ }
+
+ public function updateEcommerceItem($originalIdOrder, $newItem)
+ {
+ $updateParts = $sqlBind = array();
+ foreach ($newItem as $name => $value) {
+ $updateParts[] = $name . " = ?";
+ $sqlBind[] = $value;
+ }
+
+ $parts = implode($updateParts, ', ');
+ $table = Common::prefixTable('log_conversion_item');
+
+ $sql = "UPDATE $table SET $parts WHERE idvisit = ? AND idorder = ? AND idaction_sku = ?";
+
+ $sqlBind[] = $newItem['idvisit'];
+ $sqlBind[] = $originalIdOrder;
+ $sqlBind[] = $newItem['idaction_sku'];
+
+ $this->getDb()->query($sql, $sqlBind);
+ }
+
+ public function createVisit($visit)
+ {
+ $fields = array_keys($visit);
+ $fields = implode(", ", $fields);
+ $values = Common::getSqlStringFieldsArray($visit);
+ $table = Common::prefixTable('log_visit');
+
+ $sql = "INSERT INTO $table ($fields) VALUES ($values)";
+ $bind = array_values($visit);
+
+ $db = $this->getDb();
+ $db->query($sql, $bind);
+
+ return $db->lastInsertId();
+ }
+
+ public function updateVisit($idSite, $idVisit, $valuesToUpdate)
+ {
+ list($updateParts, $sqlBind) = $this->visitFieldsToQuery($valuesToUpdate);
+
+ $parts = implode($updateParts, ', ');
+ $table = Common::prefixTable('log_visit');
+
+ $sqlQuery = "UPDATE $table SET $parts WHERE idsite = ? AND idvisit = ?";
+
+ $sqlBind[] = $idSite;
+ $sqlBind[] = $idVisit;
+
+ $db = $this->getDb();
+ $result = $db->query($sqlQuery, $sqlBind);
+ $wasInserted = $db->rowCount($result) != 0;
+
+ if (!$wasInserted) {
+ Common::printDebug("Visitor with this idvisit wasn't found in the DB.");
+ Common::printDebug("$sqlQuery --- ");
+ Common::printDebug($sqlBind);
+ }
+
+ return $wasInserted;
+ }
+
+ public function findVisitor($idSite, $configId, $idVisitor, $fieldsToRead, $numCustomVarsToRead, $shouldMatchOneFieldOnly, $isVisitorIdToLookup, $timeLookBack, $timeLookAhead)
+ {
+ $selectCustomVariables = '';
+
+ if ($numCustomVarsToRead) {
+ for ($index = 1; $index <= $numCustomVarsToRead; $index++) {
+ $selectCustomVariables .= ', custom_var_k' . $index . ', custom_var_v' . $index;
+ }
+ }
+
+ $selectFields = implode(', ', $fieldsToRead);
+
+ $select = "SELECT $selectFields $selectCustomVariables ";
+ $from = "FROM " . Common::prefixTable('log_visit');
+
+ // Two use cases:
+ // 1) there is no visitor ID so we try to match only on config_id (heuristics)
+ // Possible causes of no visitor ID: no browser cookie support, direct Tracking API request without visitor ID passed,
+ // importing server access logs with import_logs.py, etc.
+ // In this case we use config_id heuristics to try find the visitor in tahhhe past. There is a risk to assign
+ // this page view to the wrong visitor, but this is better than creating artificial visits.
+ // 2) there is a visitor ID and we trust it (config setting trust_visitors_cookies, OR it was set using &cid= in tracking API),
+ // and in these cases, we force to look up this visitor id
+ $whereCommon = "visit_last_action_time >= ? AND visit_last_action_time <= ? AND idsite = ?";
+ $bindSql = array(
+ $timeLookBack,
+ $timeLookAhead,
+ $idSite
+ );
+
+ if ($shouldMatchOneFieldOnly) {
+ if ($isVisitorIdToLookup) {
+ $whereCommon .= ' AND idvisitor = ?';
+ $bindSql[] = $idVisitor;
+ } else {
+ $whereCommon .= ' AND config_id = ?';
+ $bindSql[] = $configId;
+ }
+
+ $sql = "$select $from
+ WHERE " . $whereCommon . "
+ ORDER BY visit_last_action_time DESC
+ LIMIT 1";
+ } // We have a config_id AND a visitor_id. We match on either of these.
+ // Why do we also match on config_id?
+ // we do not trust the visitor ID only. Indeed, some browsers, or browser addons,
+ // cause the visitor id from the 1st party cookie to be different on each page view!
+ // It is not acceptable to create a new visit every time such browser does a page view,
+ // so we also backup by searching for matching config_id.
+ // We use a UNION here so that each sql query uses its own INDEX
+ else {
+ // will use INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time)
+ $where = ' AND config_id = ? AND user_id IS NULL ';
+ $bindSql[] = $configId;
+ $sqlConfigId = "$select ,
+ 0 as priority
+ $from
+ WHERE $whereCommon $where
+ ORDER BY visit_last_action_time DESC
+ LIMIT 1
+ ";
+ // will use INDEX index_idsite_idvisitor (idsite, idvisitor)
+ $bindSql[] = $timeLookBack;
+ $bindSql[] = $timeLookAhead;
+ $bindSql[] = $idSite;
+ $where = ' AND idvisitor = ?';
+ $bindSql[] = $idVisitor;
+ $sqlVisitorId = "$select ,
+ 1 as priority
+ $from
+ WHERE $whereCommon $where
+ ORDER BY visit_last_action_time DESC
+ LIMIT 1
+ ";
+
+ // We join both queries and favor the one matching the visitor_id if it did match
+ $sql = " ( $sqlConfigId )
+ UNION
+ ( $sqlVisitorId )
+ ORDER BY priority DESC
+ LIMIT 1";
+ }
+
+ $visitRow = $this->getDb()->fetch($sql, $bindSql);
+
+ return $visitRow;
+ }
+
+ private function visitFieldsToQuery($valuesToUpdate)
+ {
+ $updateParts = array();
+ $sqlBind = array();
+
+ foreach ($valuesToUpdate as $name => $value) {
+ // Case where bind parameters don't work
+ if ($value === $name . ' + 1') {
+ //$name = 'visit_total_events'
+ //$value = 'visit_total_events + 1';
+ $updateParts[] = " $name = $value ";
+ } else {
+ $updateParts[] = $name . " = ?";
+ $sqlBind[] = $value;
+ }
+ }
+
+ return array($updateParts, $sqlBind);
+ }
+
+ private function getDb()
+ {
+ return Tracker::getDatabase();
+ }
+
+}
diff --git a/core/Tracker/PageUrl.php b/core/Tracker/PageUrl.php
index 1d8486eda0..ae55b48aac 100644
--- a/core/Tracker/PageUrl.php
+++ b/core/Tracker/PageUrl.php
@@ -49,17 +49,22 @@ class PageUrl
if (empty($parsedUrl['query'])) {
if (empty($parsedUrl['fragment'])) {
+
return UrlHelper::getParseUrlReverse($parsedUrl);
}
+
// Exclude from the hash tag as well
$queryParameters = UrlHelper::getArrayFromQueryString($parsedUrl['fragment']);
$parsedUrl['fragment'] = UrlHelper::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude);
$url = UrlHelper::getParseUrlReverse($parsedUrl);
+
return $url;
}
+
$queryParameters = UrlHelper::getArrayFromQueryString($parsedUrl['query']);
$parsedUrl['query'] = UrlHelper::getQueryStringWithExcludedParameters($queryParameters, $parametersToExclude);
$url = UrlHelper::getParseUrlReverse($parsedUrl);
+
return $url;
}
@@ -79,17 +84,15 @@ class PageUrl
);
$website = Cache::getCacheWebsiteAttributes($idSite);
- $excludedParameters = isset($website['excluded_parameters'])
- ? $website['excluded_parameters']
- : array();
+ $excludedParameters = self::getExcludedParametersFromWebsite($website);
if (!empty($excludedParameters)) {
Common::printDebug('Excluding parameters "' . implode(',', $excludedParameters) . '" from URL');
}
$parametersToExclude = array_merge($excludedParameters,
- self::$queryParametersToExclude,
- $campaignTrackingParameters);
+ self::$queryParametersToExclude,
+ $campaignTrackingParameters);
$parametersToExclude = array_map('strtolower', $parametersToExclude);
return $parametersToExclude;
@@ -152,6 +155,7 @@ class PageUrl
if (empty($parsedUrl)) {
return $parsedUrl;
}
+
if (!empty($parsedUrl['host'])) {
$parsedUrl['host'] = mb_strtolower($parsedUrl['host'], 'UTF-8');
}
@@ -174,19 +178,24 @@ class PageUrl
public static function convertMatrixUrl($originalUrl)
{
$posFirstSemiColon = strpos($originalUrl, ";");
- if ($posFirstSemiColon === false) {
+
+ if (false === $posFirstSemiColon) {
return $originalUrl;
}
+
$posQuestionMark = strpos($originalUrl, "?");
- $replace = ($posQuestionMark === false);
+ $replace = (false === $posQuestionMark);
+
if ($posQuestionMark > $posFirstSemiColon) {
$originalUrl = substr_replace($originalUrl, ";", $posQuestionMark, 1);
$replace = true;
}
+
if ($replace) {
$originalUrl = substr_replace($originalUrl, "?", strpos($originalUrl, ";"), 1);
$originalUrl = str_replace(";", "&", $originalUrl);
}
+
return $originalUrl;
}
@@ -214,6 +223,7 @@ class PageUrl
$value = urlencode(mb_convert_encoding($decoded, 'UTF-8', $encoding));
}
}
+
return $value;
}
@@ -226,6 +236,7 @@ class PageUrl
$value = PageUrl::reencodeParameterValue($value, $encoding);
}
}
+
return $queryParameters;
}
@@ -247,12 +258,13 @@ class PageUrl
{
// if query params are encoded w/ non-utf8 characters (due to browser bug or whatever),
// encode to UTF-8.
- if ($encoding !== false
- && strtolower($encoding) != 'utf-8'
+ if (false !== $encoding
+ && 'utf-8' != strtolower($encoding)
&& function_exists('mb_check_encoding')
) {
$queryParameters = PageUrl::reencodeParametersArray($queryParameters, $encoding);
}
+
return $queryParameters;
}
@@ -261,6 +273,7 @@ class PageUrl
$url = Common::unsanitizeInputValue($url);
$url = PageUrl::cleanupString($url);
$url = PageUrl::convertMatrixUrl($url);
+
return $url;
}
@@ -274,6 +287,7 @@ class PageUrl
public static function reconstructNormalizedUrl($url, $prefixId)
{
$map = array_flip(self::$urlPrefixMap);
+
if ($prefixId !== null && isset($map[$prefixId])) {
$fullUrl = $map[$prefixId] . $url;
} else {
@@ -283,7 +297,8 @@ class PageUrl
// Clean up host & hash tags, for URLs
$parsedUrl = @parse_url($fullUrl);
$parsedUrl = PageUrl::cleanupHostAndHashTag($parsedUrl);
- $url = UrlHelper::getParseUrlReverse($parsedUrl);
+ $url = UrlHelper::getParseUrlReverse($parsedUrl);
+
if (!empty($url)) {
return $url;
}
@@ -302,12 +317,14 @@ class PageUrl
{
foreach (self::$urlPrefixMap as $prefix => $id) {
if (strtolower(substr($url, 0, strlen($prefix))) == $prefix) {
+
return array(
'url' => substr($url, strlen($prefix)),
'prefixId' => $id
);
}
}
+
return array('url' => $url, 'prefixId' => null);
}
@@ -317,10 +334,20 @@ class PageUrl
if (!UrlHelper::isLookLikeUrl($url)) {
Common::printDebug("WARNING: URL looks invalid and is discarded");
- $url = false;
- return $url;
+
+ return false;
}
+
return $url;
}
+
+ private static function getExcludedParametersFromWebsite($website)
+ {
+ if (isset($website['excluded_parameters'])) {
+ return $website['excluded_parameters'];
+ }
+
+ return array();
+ }
}
diff --git a/core/Tracker/Request.php b/core/Tracker/Request.php
index b34ee88709..212abbd0d2 100644
--- a/core/Tracker/Request.php
+++ b/core/Tracker/Request.php
@@ -138,10 +138,12 @@ class Request
if (!$this->isTimestampValid($cookieFirstVisitTimestamp)) {
$cookieFirstVisitTimestamp = $this->getCurrentTimestamp();
}
+
$daysSinceFirstVisit = round(($this->getCurrentTimestamp() - $cookieFirstVisitTimestamp) / 86400, $precision = 0);
if ($daysSinceFirstVisit < 0) {
$daysSinceFirstVisit = 0;
}
+
return $daysSinceFirstVisit;
}
@@ -152,12 +154,14 @@ class Request
{
$daysSinceLastOrder = false;
$lastOrderTimestamp = $this->getParam('_ects');
+
if ($this->isTimestampValid($lastOrderTimestamp)) {
$daysSinceLastOrder = round(($this->getCurrentTimestamp() - $lastOrderTimestamp) / 86400, $precision = 0);
if ($daysSinceLastOrder < 0) {
$daysSinceLastOrder = 0;
}
}
+
return $daysSinceLastOrder;
}
@@ -168,12 +172,14 @@ class Request
{
$daysSinceLastVisit = 0;
$lastVisitTimestamp = $this->getParam('_viewts');
+
if ($this->isTimestampValid($lastVisitTimestamp)) {
$daysSinceLastVisit = round(($this->getCurrentTimestamp() - $lastVisitTimestamp) / 86400, $precision = 0);
if ($daysSinceLastVisit < 0) {
$daysSinceLastVisit = 0;
}
}
+
return $daysSinceLastVisit;
}
@@ -297,6 +303,7 @@ class Request
if (!isset($supportedParams[$name])) {
throw new Exception("Requested parameter $name is not a known Tracking API Parameter.");
}
+
$paramDefaultValue = $supportedParams[$name][0];
$paramType = $supportedParams[$name][1];
@@ -318,7 +325,7 @@ class Request
protected function isTimestampValid($time)
{
return $time <= $this->getCurrentTimestamp()
- && $time > $this->getCurrentTimestamp() - 10 * 365 * 86400;
+ && $time > $this->getCurrentTimestamp() - 10 * 365 * 86400;
}
public function getIdSite()
@@ -338,9 +345,11 @@ class Request
* request.
*/
Piwik::postEvent('Tracker.Request.getIdSite', array(&$idSite, $this->params));
+
if ($idSite <= 0) {
throw new Exception('Invalid idSite: \'' . $idSite . '\'');
}
+
return $idSite;
}
@@ -359,9 +368,11 @@ class Request
}
$customVar = Common::unsanitizeInputValues(Common::getRequestVar($parameter, '', 'json', $this->params));
+
if (!is_array($customVar)) {
return array();
}
+
$customVariables = array();
$maxCustomVars = CustomVariables::getMaxCustomVariables();
foreach ($customVar as $id => $keyValue) {
@@ -374,13 +385,14 @@ class Request
Common::printDebug("Invalid custom variables detected (id=$id)");
continue;
}
+
if (strlen($keyValue[1]) == 0) {
$keyValue[1] = "";
}
// We keep in the URL when Custom Variable have empty names
// and values, as it means they can be deleted server side
- $key = self::truncateCustomVariable($keyValue[0]);
+ $key = self::truncateCustomVariable($keyValue[0]);
$value = self::truncateCustomVariable($keyValue[1]);
$customVariables['custom_var_k' . $id] = $key;
$customVariables['custom_var_v' . $id] = $value;
@@ -407,6 +419,7 @@ class Request
if (!$this->shouldUseThirdPartyCookie()) {
return;
}
+
Common::printDebug("We manage the cookie...");
$cookie = $this->makeThirdPartyCookie();
@@ -455,7 +468,7 @@ class Request
// If User ID is set it takes precedence
$userId = $this->getForcedUserId();
- if($userId) {
+ if ($userId) {
$userIdHashed = $this->getUserIdHashed($userId);
$idVisitor = $this->truncateIdAsVisitorId($userIdHashed);
Common::printDebug("Request will be recorded for this user_id = " . $userId . " (idvisitor = $idVisitor)");
@@ -502,6 +515,7 @@ class Request
return $binVisitorId;
}
}
+
return false;
}
@@ -512,6 +526,7 @@ class Request
} else {
$ipString = IP::getIpFromHeader();
}
+
$ip = IP::P2N($ipString);
return $ip;
}
@@ -536,10 +551,11 @@ class Request
public function getForcedUserId()
{
$userId = $this->getParam('uid');
- if(strlen($userId) > 0) {
+ if (strlen($userId) > 0) {
return $userId;
}
- return null;
+
+ return false;
}
public function getForcedVisitorId()
@@ -572,6 +588,7 @@ class Request
) {
return (int)$generationTime;
}
+
return false;
}
@@ -590,8 +607,8 @@ class Request
* @param $userId
* @return string
*/
- private function getUserIdHashed($userId)
+ public function getUserIdHashed($userId)
{
- return sha1($userId);
+ return substr( sha1( $userId ), 0, 16);
}
}
diff --git a/core/Tracker/TableLogAction.php b/core/Tracker/TableLogAction.php
index c547d742b8..4f4849a352 100644
--- a/core/Tracker/TableLogAction.php
+++ b/core/Tracker/TableLogAction.php
@@ -36,35 +36,22 @@ class TableLogAction
{
// Add url prefix if not set
foreach($actionsNameAndType as &$action) {
- if(count($action) == 2) {
+ if (2 == count($action)) {
$action[] = null;
}
}
+
$actionIds = self::queryIdsAction($actionsNameAndType);
list($queriedIds, $fieldNamesToInsert) = self::processIdsToInsert($actionsNameAndType, $actionIds);
$insertedIds = self::insertNewIdsAction($actionsNameAndType, $fieldNamesToInsert);
-
- $queriedIds = $queriedIds + $insertedIds;
+ $queriedIds = $queriedIds + $insertedIds;
return $queriedIds;
}
/**
- * @param $name
- * @param $type
- * @return string
- */
- private static function getIdActionMatchingNameAndType($name, $type)
- {
- $sql = TableLogAction::getSqlSelectActionId();
- $bind = array($name, $name, $type);
- $idAction = \Piwik\Db::fetchOne($sql, $bind);
- return $idAction;
- }
-
- /**
* @param $matchType
* @param $actionType
* @return string
@@ -75,6 +62,7 @@ class TableLogAction
// now, we handle the cases =@ (contains) and !@ (does not contain)
// build the expression based on the match type
$sql = 'SELECT idaction FROM ' . Common::prefixTable('log_action') . ' WHERE %s AND type = ' . $actionType . ' )';
+
switch ($matchType) {
case '=@':
// use concat to make sure, no %s occurs because some plugins use %s in their sql
@@ -87,61 +75,45 @@ class TableLogAction
throw new \Exception("This match type $matchType is not available for action-segments.");
break;
}
+
$sql = sprintf($sql, $where);
- return $sql;
- }
- private static function getSqlSelectActionId()
- {
- $sql = "SELECT idaction, type, name
- FROM " . Common::prefixTable('log_action')
- . " WHERE "
- . " ( hash = CRC32(?) AND name = ? AND type = ? ) ";
return $sql;
}
private static function insertNewIdsAction($actionsNameAndType, $fieldNamesToInsert)
{
- $sql = "INSERT INTO " . Common::prefixTable('log_action') .
- "( name, hash, type, url_prefix ) VALUES (?,CRC32(?),?,?)";
// Then, we insert all new actions in the lookup table
$inserted = array();
+
foreach ($fieldNamesToInsert as $fieldName) {
list($name, $type, $urlPrefix) = $actionsNameAndType[$fieldName];
- Tracker::getDatabase()->query($sql, array($name, $name, $type, $urlPrefix));
- $actionId = Tracker::getDatabase()->lastInsertId();
-
- $inserted[$fieldName] = $actionId;
+ $actionId = self::getModel()->createNewIdAction($name, $type, $urlPrefix);
Common::printDebug("Recorded a new action (" . Action::getTypeAsString($type) . ") in the lookup table: " . $name . " (idaction = " . $actionId . ")");
+
+ $inserted[$fieldName] = $actionId;
}
+
return $inserted;
}
+ private static function getModel()
+ {
+ return new Model();
+ }
+
private static function queryIdsAction($actionsNameAndType)
{
- $sql = TableLogAction::getSqlSelectActionId();
- $bind = array();
- $i = 0;
+ $toQuery = array();
foreach ($actionsNameAndType as &$actionNameType) {
list($name, $type, $urlPrefix) = $actionNameType;
- if (empty($name)) {
- continue;
- }
- if ($i > 0) {
- $sql .= " OR ( hash = CRC32(?) AND name = ? AND type = ? ) ";
- }
- $bind[] = $name;
- $bind[] = $name;
- $bind[] = $type;
- $i++;
+ $toQuery[] = array('name' => $name, 'type' => $type);
}
- // Case URL & Title are empty
- if (empty($bind)) {
- return false;
- }
- $actionIds = Tracker::getDatabase()->fetchAll($sql, $bind);
+
+ $actionIds = self::getModel()->getIdsAction($toQuery);
+
return $actionIds;
}
@@ -150,6 +122,7 @@ class TableLogAction
// For the Actions found in the lookup table, add the idaction in the array,
// If not found in lookup table, queue for INSERT
$fieldNamesToInsert = $fieldNameToActionId = array();
+
foreach ($actionsNameAndType as $fieldName => &$actionNameType) {
@list($name, $type, $urlPrefix) = $actionNameType;
if (empty($name)) {
@@ -172,6 +145,7 @@ class TableLogAction
$fieldNamesToInsert[] = $fieldName;
}
}
+
return array($fieldNameToActionId, $fieldNamesToInsert);
}
@@ -197,12 +171,13 @@ class TableLogAction
// for urls trim protocol and www because it is not recorded in the db
$valueToMatch = preg_replace('@^http[s]?://(www\.)?@i', '', $valueToMatch);
}
+
$valueToMatch = Common::sanitizeInputValue(Common::unsanitizeInputValue($valueToMatch));
if ($matchType == SegmentExpression::MATCH_EQUAL
|| $matchType == SegmentExpression::MATCH_NOT_EQUAL
) {
- $idAction = self::getIdActionMatchingNameAndType($valueToMatch, $actionType);
+ $idAction = self::getModel()->getIdActionMatchingNameAndType($valueToMatch, $actionType);
// if the action is not found, we hack -100 to ensure it tries to match against an integer
// otherwise binding idaction_name to "false" returns some rows for some reasons (in case &segment=pageTitle==Větrnásssssss)
if (empty($idAction)) {
@@ -214,6 +189,7 @@ class TableLogAction
// "name contains $string" match can match several idaction so we cannot return yet an idaction
// special case
$sql = TableLogAction::getSelectQueryWhereNameContains($matchType, $actionType);
+
return array(
// mark that the returned value is an sql-expression instead of a literal value
'SQL' => $sql,
@@ -229,15 +205,16 @@ class TableLogAction
private static function guessActionTypeFromSegment($segmentName)
{
$exactMatch = array(
- 'eventAction' => Action::TYPE_EVENT_ACTION,
- 'eventCategory' => Action::TYPE_EVENT_CATEGORY,
- 'eventName' => Action::TYPE_EVENT_NAME,
- 'contentPiece' => Action::TYPE_CONTENT_PIECE,
- 'contentTarget' => Action::TYPE_CONTENT_TARGET,
- 'contentName' => Action::TYPE_CONTENT_NAME,
+ 'eventAction' => Action::TYPE_EVENT_ACTION,
+ 'eventCategory' => Action::TYPE_EVENT_CATEGORY,
+ 'eventName' => Action::TYPE_EVENT_NAME,
+ 'contentPiece' => Action::TYPE_CONTENT_PIECE,
+ 'contentTarget' => Action::TYPE_CONTENT_TARGET,
+ 'contentName' => Action::TYPE_CONTENT_NAME,
'contentInteraction' => Action::TYPE_CONTENT_INTERACTION,
);
- if(!empty($exactMatch[$segmentName])) {
+
+ if (!empty($exactMatch[$segmentName])) {
return $exactMatch[$segmentName];
}
diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php
index 90b58a88b0..0ce45c3659 100644
--- a/core/Tracker/Visit.php
+++ b/core/Tracker/Visit.php
@@ -109,8 +109,9 @@ class Visit implements VisitInterface
$visitIsConverted = false;
$action = null;
- $requestIsManualGoalConversion = ($this->goalManager->idGoal > 0);
+ $isManualGoalConversion = $this->goalManager->isManualGoalConversion();
$requestIsEcommerce = $this->goalManager->requestIsEcommerce;
+
if ($requestIsEcommerce) {
$someGoalsConverted = true;
@@ -118,23 +119,26 @@ class Visit implements VisitInterface
if ($this->goalManager->isGoalAnOrder()) {
$visitIsConverted = true;
}
- } // this request is from the JS call to piwikTracker.trackGoal()
- elseif ($requestIsManualGoalConversion) {
+
+ } elseif ($isManualGoalConversion) {
+ // this request is from the JS call to piwikTracker.trackGoal()
$someGoalsConverted = $this->goalManager->detectGoalId($this->request->getIdSite());
- $visitIsConverted = $someGoalsConverted;
+ $visitIsConverted = $someGoalsConverted;
+
// if we find a idgoal in the URL, but then the goal is not valid, this is most likely a fake request
if (!$someGoalsConverted) {
Common::printDebug('Invalid goal tracking request for goal id = ' . $this->goalManager->idGoal);
return;
}
- } // normal page view, potentially triggering a URL matching goal
- else {
+
+ } else {
+ // normal page view, potentially triggering a URL matching goal
$action = Action::factory($this->request);
$action->writeDebugInfo();
$someGoalsConverted = $this->goalManager->detectGoalsMatchingUrl($this->request->getIdSite(), $action);
- $visitIsConverted = $someGoalsConverted;
+ $visitIsConverted = $someGoalsConverted;
$action->loadIdsFromLogActionTable();
}
@@ -153,6 +157,7 @@ class Visit implements VisitInterface
if (!$isLastActionInTheSameVisit) {
Common::printDebug("Visitor detected, but last action was more than 30 minutes ago...");
}
+
// Known visit when:
// ( - the visitor has the Piwik cookie with the idcookie ID used by Piwik to match the visitor
// OR
@@ -165,12 +170,15 @@ class Visit implements VisitInterface
) {
$idReferrerActionUrl = $this->visitorInfo['visit_exit_idaction_url'];
$idReferrerActionName = $this->visitorInfo['visit_exit_idaction_name'];
+
try {
$this->goalManager->detectIsThereExistingCartInVisit($this->visitorInfo);
$this->handleExistingVisit($visitor, $action, $visitIsConverted);
+
if (!is_null($action)) {
$action->record($visitor, $idReferrerActionUrl, $idReferrerActionName);
}
+
} catch (VisitorNotFoundInDb $e) {
// There is an edge case when:
@@ -179,7 +187,7 @@ class Visit implements VisitInterface
// because the UPDATE didn't affect any rows (one row was found, but not updated since no field changed)
// - the exception is caught here and will result in a new visit incorrectly
// In this case, we cancel the current conversion to be recorded:
- if ($requestIsManualGoalConversion
+ if ($isManualGoalConversion
|| $requestIsEcommerce
) {
$someGoalsConverted = $visitIsConverted = false;
@@ -239,8 +247,7 @@ class Visit implements VisitInterface
// TODO we should not have to sync this->visitorInfo and $visitor columns.
// TODO it should be its own dimension
- $this->visitorInfo['time_spent_ref_action'] = $this->getTimeSpentReferrerAction();
- $visitor->setVisitorColumn('time_spent_ref_action', $this->visitorInfo['time_spent_ref_action']);
+ $this->setVisitorColumn($visitor, 'time_spent_ref_action', $this->getTimeSpentReferrerAction());
// update visitorInfo
foreach ($valuesToUpdate as $name => $value) {
@@ -262,8 +269,7 @@ class Visit implements VisitInterface
$this->updateExistingVisit($valuesToUpdate);
- $this->visitorInfo['visit_last_action_time'] = $this->request->getCurrentTimestamp();
- $visitor->setVisitorColumn('visit_last_action_time', $this->visitorInfo['visit_last_action_time']);
+ $this->setVisitorColumn($visitor, 'visit_last_action_time', $this->request->getCurrentTimestamp());
}
/**
@@ -275,8 +281,8 @@ class Visit implements VisitInterface
if ($timeSpent < 0) {
$timeSpent = 0;
}
- $visitStandardLength = Config::getInstance()->Tracker['visit_standard_length'];
- if($timeSpent > $visitStandardLength) {
+ $visitStandardLength = $this->getVisitStandardLength();
+ if ($timeSpent > $visitStandardLength) {
$timeSpent = $visitStandardLength;
}
return $timeSpent;
@@ -297,15 +303,7 @@ class Visit implements VisitInterface
{
Common::printDebug("New Visit (IP = " . IP::N2P($this->getVisitorIp()) . ")");
- $this->visitorInfo = $this->getNewVisitorInformation($visitor);
-
- // Add Custom variable key,value to the visitor array
- $this->visitorInfo = array_merge($this->visitorInfo, $this->visitorCustomVariables);
-
- $visitor->clearVisitorInfo();
- foreach ($this->visitorInfo as $key => $value) {
- $visitor->setVisitorColumn($key, $value);
- }
+ $this->setNewVisitorInformation($visitor);
$dimensions = $this->getAllVisitDimensions();
@@ -329,15 +327,16 @@ class Visit implements VisitInterface
$this->printVisitorInformation();
- $idVisit = $this->insertNewVisit( $this->visitorInfo );
+ $idVisit = $this->insertNewVisit($this->visitorInfo);
- $this->visitorInfo['idvisit'] = $idVisit;
- $this->visitorInfo['visit_first_action_time'] = $this->request->getCurrentTimestamp();
- $this->visitorInfo['visit_last_action_time'] = $this->request->getCurrentTimestamp();
+ $this->setVisitorColumn($visitor, 'idvisit', $idVisit);
+ $this->setVisitorColumn($visitor, 'visit_first_action_time', $this->request->getCurrentTimestamp());
+ $this->setVisitorColumn($visitor, 'visit_last_action_time', $this->request->getCurrentTimestamp());
+ }
- $visitor->setVisitorColumn('idvisit', $this->visitorInfo['idvisit']);
- $visitor->setVisitorColumn('visit_first_action_time', $this->visitorInfo['visit_first_action_time']);
- $visitor->setVisitorColumn('visit_last_action_time', $this->visitorInfo['visit_last_action_time']);
+ private function getModel()
+ {
+ return new Model();
}
/**
@@ -353,7 +352,7 @@ class Visit implements VisitInterface
// If the visitor had a first party ID cookie, then we use this value
if (!empty($this->visitorInfo['idvisitor'])
- && strlen($this->visitorInfo['idvisitor']) == Tracker::LENGTH_BINARY_ID
+ && Tracker::LENGTH_BINARY_ID == strlen($this->visitorInfo['idvisitor'])
) {
return $this->visitorInfo['idvisitor'];
}
@@ -366,8 +365,7 @@ class Visit implements VisitInterface
*/
public static function generateUniqueVisitorId()
{
- $uniqueId = substr(Common::generateUniqId(), 0, Tracker::LENGTH_HEX_ID_STRING);
- return $uniqueId;
+ return substr(Common::generateUniqId(), 0, Tracker::LENGTH_HEX_ID_STRING);
}
/**
@@ -387,9 +385,10 @@ class Visit implements VisitInterface
*/
protected function getSettingsObject()
{
- if(is_null($this->userSettings)) {
+ if (is_null($this->userSettings)) {
$this->userSettings = new Settings( $this->request, $this->getVisitorIp() );
}
+
return $this->userSettings;
}
@@ -410,33 +409,26 @@ class Visit implements VisitInterface
public static function isHostKnownAliasHost($urlHost, $idSite)
{
$websiteData = Cache::getCacheWebsiteAttributes($idSite);
+
if (isset($websiteData['hosts'])) {
$canonicalHosts = array();
foreach ($websiteData['hosts'] as $host) {
- $canonicalHosts[] = str_replace('www.', '', mb_strtolower($host, 'UTF-8'));
+ $canonicalHosts[] = self::toCanonicalHost($host);
}
- $canonicalHost = str_replace('www.', '', mb_strtolower($urlHost, 'UTF-8'));
+
+ $canonicalHost = self::toCanonicalHost($urlHost);
if (in_array($canonicalHost, $canonicalHosts)) {
return true;
}
}
+
return false;
}
- /**
- * @return mixed
- */
- protected function insertNewVisit($visit)
+ private static function toCanonicalHost($host)
{
- $fields = implode(", ", array_keys($visit));
- $values = Common::getSqlStringFieldsArray($visit);
-
- $sql = "INSERT INTO " . Common::prefixTable('log_visit') . " ($fields) VALUES ($values)";
- $bind = array_values($visit);
- Tracker::getDatabase()->query($sql, $bind);
-
- $idVisit = Tracker::getDatabase()->lastInsertId();
- return $idVisit;
+ $hostLower = mb_strtolower($host, 'UTF-8');
+ return str_replace('www.', '', $hostLower);
}
/**
@@ -445,45 +437,32 @@ class Visit implements VisitInterface
*/
protected function updateExistingVisit($valuesToUpdate)
{
- $sqlQuery = "UPDATE " . Common::prefixTable('log_visit') . "
- SET %s
- WHERE idsite = ?
- AND idvisit = ?";
- // build sql query
- $updateParts = $sqlBind = array();
- foreach ($valuesToUpdate as $name => $value) {
- // Case where bind parameters don't work
- if(strpos($value, $name) !== false) {
- //$name = 'visit_total_events'
- //$value = 'visit_total_events + 1';
- $updateParts[] = " $name = $value ";
- } else {
- $updateParts[] = $name . " = ?";
- $sqlBind[] = $value;
- }
- }
- $sqlQuery = sprintf($sqlQuery, implode($updateParts, ', ') );
- array_push($sqlBind, $this->request->getIdSite(), (int)$this->visitorInfo['idvisit']);
+ $idSite = $this->request->getIdSite();
+ $idVisit = (int) $this->visitorInfo['idvisit'];
- $result = Tracker::getDatabase()->query($sqlQuery, $sqlBind);
+ $wasInserted = $this->getModel()->updateVisit($idSite, $idVisit, $valuesToUpdate);
// Debug output
if (isset($valuesToUpdate['idvisitor'])) {
$valuesToUpdate['idvisitor'] = bin2hex($valuesToUpdate['idvisitor']);
}
- Common::printDebug('Updating existing visit: ' . var_export($valuesToUpdate, true));
- if (Tracker::getDatabase()->rowCount($result) == 0) {
- Common::printDebug("Visitor with this idvisit wasn't found in the DB.");
- Common::printDebug("$sqlQuery --- ");
- Common::printDebug($sqlBind);
+ if ($wasInserted) {
+ Common::printDebug('Updated existing visit: ' . var_export($valuesToUpdate, true));
+ } else {
throw new VisitorNotFoundInDb(
"The visitor with idvisitor=" . bin2hex($this->visitorInfo['idvisitor']) . " and idvisit=" . $this->visitorInfo['idvisit']
. " wasn't found in the DB, we fallback to a new visitor");
}
}
- protected function printVisitorInformation()
+ private function setVisitorColumn(Visitor $visitor, $key, $value)
+ {
+ $this->visitorInfo[$key] = $value;
+ $visitor->setVisitorColumn($key, $value);
+ }
+
+ private function printVisitorInformation()
{
$debugVisitInfo = $this->visitorInfo;
$debugVisitInfo['idvisitor'] = bin2hex($debugVisitInfo['idvisitor']);
@@ -491,13 +470,22 @@ class Visit implements VisitInterface
Common::printDebug($debugVisitInfo);
}
- protected function getNewVisitorInformation($visitor)
+ private function setNewVisitorInformation(Visitor $visitor)
{
- return array(
- 'idvisitor' => $this->getVisitorIdcookie($visitor),
- 'config_id' => $this->getSettingsObject()->getConfigId(),
- 'location_ip' => $this->getVisitorIp(),
- );
+ $idVisitor = $this->getVisitorIdcookie($visitor);
+ $visitorIp = $this->getVisitorIp();
+ $configId = $this->getSettingsObject()->getConfigId();
+
+ $this->visitorInfo = array();
+ $visitor->clearVisitorInfo();
+
+ $this->setVisitorColumn($visitor, 'idvisitor', $idVisitor);
+ $this->setVisitorColumn($visitor, 'config_id', $configId);
+ $this->setVisitorColumn($visitor, 'location_ip', $visitorIp);
+
+ foreach ($this->visitorCustomVariables as $key => $value) {
+ $this->setVisitorColumn($visitor, $key, $value);
+ }
}
/**
@@ -508,15 +496,11 @@ class Visit implements VisitInterface
* @param $visitIsConverted
* @return array
*/
- protected function getExistingVisitFieldsToUpdate($visitor, $action, $visitIsConverted)
+ private function getExistingVisitFieldsToUpdate($visitor, $action, $visitIsConverted)
{
$valuesToUpdate = array();
- // Might update the idvisitor when it was forced or overwritten for this visit
- if (strlen($this->visitorInfo['idvisitor']) == Tracker::LENGTH_BINARY_ID) {
- $valuesToUpdate['idvisitor'] = $this->visitorInfo['idvisitor'];
- $visitor->setVisitorColumn('idvisitor', $this->visitorInfo['idvisitor']);
- }
+ $valuesToUpdate = $this->setIdVisitorForExistingVisit($visitor, $valuesToUpdate);
$dimensions = $this->getAllVisitDimensions();
$valuesToUpdate = $this->triggerHookOnDimensions($dimensions, 'onExistingVisit', $visitor, $action, $valuesToUpdate);
@@ -526,8 +510,7 @@ class Visit implements VisitInterface
}
// Custom Variables overwrite previous values on each page view
- $valuesToUpdate = array_merge($valuesToUpdate, $this->visitorCustomVariables);
- return $valuesToUpdate;
+ return array_merge($valuesToUpdate, $this->visitorCustomVariables);
}
/**
@@ -567,7 +550,44 @@ class Visit implements VisitInterface
foreach($dimensions as $dimension) {
$dimensionNames[] = $dimension->getColumnName();
}
+
Common::printDebug("Following dimensions have been collected from plugins: " . implode(", ", $dimensionNames));
+
return $dimensions;
}
+
+ private function getVisitStandardLength()
+ {
+ return Config::getInstance()->Tracker['visit_standard_length'];
+ }
+
+ /**
+ * @param $visitor
+ * @param $valuesToUpdate
+ * @return mixed
+ */
+ private function setIdVisitorForExistingVisit($visitor, $valuesToUpdate)
+ {
+ // Might update the idvisitor when it was forced or overwritten for this visit
+ if (strlen($this->visitorInfo['idvisitor']) == Tracker::LENGTH_BINARY_ID) {
+ $binIdVisitor = $this->visitorInfo['idvisitor'];
+ $visitor->setVisitorColumn('idvisitor', $binIdVisitor);
+ $valuesToUpdate['idvisitor'] = $binIdVisitor;
+ }
+
+ // User ID takes precedence and overwrites idvisitor value
+ $userId = $this->request->getForcedUserId();
+ if ($userId) {
+ $userIdHash = $this->request->getUserIdHashed($userId);
+ $binIdVisitor = Common::hex2bin($userIdHash);
+ $visitor->setVisitorColumn('idvisitor', $binIdVisitor);
+ $valuesToUpdate['idvisitor'] = $binIdVisitor;
+ }
+ return $valuesToUpdate;
+ }
+
+ protected function insertNewVisit($visit)
+ {
+ return $this->getModel()->createVisit($visit);
+ }
}
diff --git a/core/Tracker/VisitExcluded.php b/core/Tracker/VisitExcluded.php
index 586c874a0c..48eb1e3db7 100644
--- a/core/Tracker/VisitExcluded.php
+++ b/core/Tracker/VisitExcluded.php
@@ -8,7 +8,6 @@
*/
namespace Piwik\Tracker;
-use DeviceDetector\Parser\Bot;
use Piwik\Common;
use Piwik\Config;
use Piwik\DeviceDetectorFactory;
@@ -27,16 +26,16 @@ class VisitExcluded
*/
public function __construct(Request $request, $ip = false, $userAgent = false)
{
- if ($ip === false) {
+ if (false === $ip) {
$ip = $request->getIp();
}
- if ($userAgent === false) {
+ if (false === $userAgent) {
$userAgent = $request->getUserAgent();
}
- $this->request = $request;
- $this->idSite = $request->getIdSite();
+ $this->request = $request;
+ $this->idSite = $request->getIdSite();
$this->userAgent = $userAgent;
$this->ip = $ip;
}
@@ -200,6 +199,7 @@ class VisitExcluded
Common::printDebug('Piwik ignore cookie was found, visit not tracked.');
return true;
}
+
return false;
}
@@ -211,12 +211,14 @@ class VisitExcluded
protected function isVisitorIpExcluded()
{
$websiteAttributes = Cache::getCacheWebsiteAttributes($this->idSite);
+
if (!empty($websiteAttributes['excluded_ips'])) {
if (IP::isIpInRange($this->ip, $websiteAttributes['excluded_ips'])) {
Common::printDebug('Visitor IP ' . IP::N2P($this->ip) . ' is excluded from being tracked');
return true;
}
}
+
return false;
}
@@ -232,6 +234,7 @@ class VisitExcluded
protected function isUserAgentExcluded()
{
$websiteAttributes = Cache::getCacheWebsiteAttributes($this->idSite);
+
if (!empty($websiteAttributes['excluded_user_agents'])) {
foreach ($websiteAttributes['excluded_user_agents'] as $excludedUserAgent) {
// if the excluded user agent string part is in this visit's user agent, this visit should be excluded
@@ -240,6 +243,7 @@ class VisitExcluded
}
}
}
+
return false;
}
@@ -254,12 +258,14 @@ class VisitExcluded
$spamHosts = explode(",", $spamHosts);
$referrerUrl = $this->request->getParam('urlref');
+
foreach($spamHosts as $spamHost) {
- if( strpos($referrerUrl, $spamHost) !== false) {
+ if ( strpos($referrerUrl, $spamHost) !== false) {
Common::printDebug('Referrer URL is a known spam: ' . $spamHost);
return true;
}
}
+
return false;
}
}
diff --git a/core/Tracker/Visitor.php b/core/Tracker/Visitor.php
index b412078151..afec61849e 100644
--- a/core/Tracker/Visitor.php
+++ b/core/Tracker/Visitor.php
@@ -25,9 +25,9 @@ class Visitor
public function __construct(Request $request, $configId, $visitorInfo = array(), $customVariables = null)
{
$this->request = $request;
+ $this->configId = $configId;
$this->visitorInfo = $visitorInfo;
$this->customVariables = $customVariables;
- $this->configId = $configId;
}
/**
@@ -41,119 +41,35 @@ class Visitor
{
$this->setIsVisitorKnown(false);
- $configId = $this->configId;
-
+ $configId = $this->configId;
+ $idSite = $this->request->getIdSite();
$idVisitor = $this->request->getVisitorId();
+
$isVisitorIdToLookup = !empty($idVisitor);
if ($isVisitorIdToLookup) {
$this->visitorInfo['idvisitor'] = $idVisitor;
- Common::printDebug("Matching visitors with: visitorId=" . bin2hex($this->visitorInfo['idvisitor']) . " OR configId=" . bin2hex($configId));
+ Common::printDebug("Matching visitors with: visitorId=" . bin2hex($idVisitor) . " OR configId=" . bin2hex($configId));
} else {
Common::printDebug("Visitor doesn't have the piwik cookie...");
}
- $selectCustomVariables = '';
- // No custom var were found in the request, so let's copy the previous one in a potential conversion later
+ $numCustomVarsToRead = 0;
if (!$this->customVariables) {
- $maxCustomVariables = CustomVariables::getMaxCustomVariables();
-
- for ($index = 1; $index <= $maxCustomVariables; $index++) {
- $selectCustomVariables .= ', custom_var_k' . $index . ', custom_var_v' . $index;
- }
+ // No custom var were found in the request, so let's copy the previous one in a potential conversion later
+ $numCustomVarsToRead = CustomVariables::getMaxCustomVariables();
}
- $persistedVisitAttributes = self::getVisitFieldsPersist();
- array_unshift($persistedVisitAttributes, 'visit_first_action_time');
- array_unshift($persistedVisitAttributes, 'visit_last_action_time');
- $persistedVisitAttributes = array_unique($persistedVisitAttributes);
-
- $selectFields = implode(", ", $persistedVisitAttributes);
-
- $select = "SELECT
- $selectFields
- $selectCustomVariables
- ";
- $from = "FROM " . Common::prefixTable('log_visit');
-
+ $persistedVisitAttributes = $this->getVisitFieldsPersist();
+ $shouldMatchOneFieldOnly = $this->shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup);
list($timeLookBack, $timeLookAhead) = $this->getWindowLookupThisVisit();
- $shouldMatchOneFieldOnly = $this->shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup);
-
- // Two use cases:
- // 1) there is no visitor ID so we try to match only on config_id (heuristics)
- // Possible causes of no visitor ID: no browser cookie support, direct Tracking API request without visitor ID passed,
- // importing server access logs with import_logs.py, etc.
- // In this case we use config_id heuristics to try find the visitor in tahhhe past. There is a risk to assign
- // this page view to the wrong visitor, but this is better than creating artificial visits.
- // 2) there is a visitor ID and we trust it (config setting trust_visitors_cookies, OR it was set using &cid= in tracking API),
- // and in these cases, we force to look up this visitor id
- $whereCommon = "visit_last_action_time >= ? AND visit_last_action_time <= ? AND idsite = ?";
- $bindSql = array(
- $timeLookBack,
- $timeLookAhead,
- $this->request->getIdSite()
- );
-
- if ($shouldMatchOneFieldOnly) {
- if ($isVisitorIdToLookup) {
- $whereCommon .= ' AND idvisitor = ?';
- $bindSql[] = $this->visitorInfo['idvisitor'];
- } else {
- $whereCommon .= ' AND config_id = ?';
- $bindSql[] = $configId;
- }
-
- $sql = "$select
- $from
- WHERE " . $whereCommon . "
- ORDER BY visit_last_action_time DESC
- LIMIT 1";
- } // We have a config_id AND a visitor_id. We match on either of these.
- // Why do we also match on config_id?
- // we do not trust the visitor ID only. Indeed, some browsers, or browser addons,
- // cause the visitor id from the 1st party cookie to be different on each page view!
- // It is not acceptable to create a new visit every time such browser does a page view,
- // so we also backup by searching for matching config_id.
- // We use a UNION here so that each sql query uses its own INDEX
- else {
- // will use INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time)
- $where = ' AND config_id = ?';
- $bindSql[] = $configId;
- $sqlConfigId = "$select ,
- 0 as priority
- $from
- WHERE $whereCommon $where
- ORDER BY visit_last_action_time DESC
- LIMIT 1
- ";
- // will use INDEX index_idsite_idvisitor (idsite, idvisitor)
- $bindSql[] = $timeLookBack;
- $bindSql[] = $timeLookAhead;
- $bindSql[] = $this->request->getIdSite();
- $where = ' AND idvisitor = ?';
- $bindSql[] = $this->visitorInfo['idvisitor'];
- $sqlVisitorId = "$select ,
- 1 as priority
- $from
- WHERE $whereCommon $where
- ORDER BY visit_last_action_time DESC
- LIMIT 1
- ";
-
- // We join both queries and favor the one matching the visitor_id if it did match
- $sql = " ( $sqlConfigId )
- UNION
- ( $sqlVisitorId )
- ORDER BY priority DESC
- LIMIT 1";
- }
-
- $visitRow = Tracker::getDatabase()->fetch($sql, $bindSql);
+ $model = $this->getModel();
+ $visitRow = $model->findVisitor($idSite, $configId, $idVisitor, $persistedVisitAttributes, $numCustomVarsToRead, $shouldMatchOneFieldOnly, $isVisitorIdToLookup, $timeLookBack, $timeLookAhead);
$isNewVisitForced = $this->request->getParam('new_visit');
$isNewVisitForced = !empty($isNewVisitForced);
- $enforceNewVisit = $isNewVisitForced || Config::getInstance()->Debug['tracker_always_new_visitor'];
+ $enforceNewVisit = $isNewVisitForced || Config::getInstance()->Debug['tracker_always_new_visitor'];
if (!$enforceNewVisit
&& $visitRow
@@ -161,17 +77,16 @@ class Visitor
) {
// These values will be used throughout the request
- foreach($persistedVisitAttributes as $field) {
+ foreach ($persistedVisitAttributes as $field) {
$this->visitorInfo[$field] = $visitRow[$field];
}
- $this->visitorInfo['visit_last_action_time'] = strtotime($visitRow['visit_last_action_time']);
+ $this->visitorInfo['visit_last_action_time'] = strtotime($visitRow['visit_last_action_time']);
$this->visitorInfo['visit_first_action_time'] = strtotime($visitRow['visit_first_action_time']);
// Custom Variables copied from Visit in potential later conversion
- if (!empty($selectCustomVariables)) {
- $maxCustomVariables = CustomVariables::getMaxCustomVariables();
- for ($i = 1; $i <= $maxCustomVariables; $i++) {
+ if (!empty($numCustomVarsToRead)) {
+ for ($i = 1; $i <= $numCustomVarsToRead; $i++) {
if (isset($visitRow['custom_var_k' . $i])
&& strlen($visitRow['custom_var_k' . $i])
) {
@@ -192,7 +107,6 @@ class Visitor
last action = " . date("r", $this->visitorInfo['visit_last_action_time']) . ",
first action = " . date("r", $this->visitorInfo['visit_first_action_time']) . ",
visit_goal_buyer' = " . $this->visitorInfo['visit_goal_buyer'] . ")");
- //Common::printDebug($this->visitorInfo);
} else {
Common::printDebug("The visitor was not matched with an existing visitor...");
}
@@ -209,16 +123,16 @@ class Visitor
*/
protected function getWindowLookupThisVisit()
{
- $visitStandardLength = Config::getInstance()->Tracker['visit_standard_length'];
+ $visitStandardLength = Config::getInstance()->Tracker['visit_standard_length'];
$lookBackNSecondsCustom = Config::getInstance()->Tracker['window_look_back_for_visitor'];
$lookAheadNSeconds = $visitStandardLength;
- $lookBackNSeconds = $visitStandardLength;
+ $lookBackNSeconds = $visitStandardLength;
if ($lookBackNSecondsCustom > $lookBackNSeconds) {
$lookBackNSeconds = $lookBackNSecondsCustom;
}
- $timeLookBack = date('Y-m-d H:i:s', $this->request->getCurrentTimestamp() - $lookBackNSeconds);
+ $timeLookBack = date('Y-m-d H:i:s', $this->request->getCurrentTimestamp() - $lookBackNSeconds);
$timeLookAhead = date('Y-m-d H:i:s', $this->request->getCurrentTimestamp() + $lookAheadNSeconds);
return array($timeLookBack, $timeLookAhead);
@@ -226,27 +140,38 @@ class Visitor
protected function shouldLookupOneVisitorFieldOnly($isVisitorIdToLookup)
{
+ $isForcedUserIdMustMatch = (false !== $this->request->getForcedUserId());
+
+ if ($isForcedUserIdMustMatch) {
+ // if &iud was set, we must try and match both idvisitor and config_id
+ return false;
+ }
+
// This setting would be enabled for Intranet websites, to ensure that visitors using all the same computer config, same IP
// are not counted as 1 visitor. In this case, we want to enforce and trust the visitor ID from the cookie.
$trustCookiesOnly = Config::getInstance()->Tracker['trust_visitors_cookies'];
+ if ($isVisitorIdToLookup && $trustCookiesOnly) {
+ return true;
+ }
// If a &cid= was set, we force to select this visitor (or create a new one)
$isForcedVisitorIdMustMatch = ($this->request->getForcedVisitorId() != null);
- // if &iud was set, we force to select this visitor (or create new one)
- $isForcedUserIdMustMatch = ($this->request->getForcedUserId() !== null);
+ if ($isForcedVisitorIdMustMatch) {
+ return true;
+ }
- $shouldMatchOneFieldOnly = (($isVisitorIdToLookup && $trustCookiesOnly)
- || $isForcedVisitorIdMustMatch
- || $isForcedUserIdMustMatch
- || !$isVisitorIdToLookup);
- return $shouldMatchOneFieldOnly;
+ if (!$isVisitorIdToLookup ) {
+ return true;
+ }
+
+ return false;
}
/**
* @return array
*/
- public static function getVisitFieldsPersist()
+ private function getVisitFieldsPersist()
{
$fields = array(
'idvisitor',
@@ -296,6 +221,10 @@ class Visitor
*/
Piwik::postEvent('Tracker.getVisitFieldsToPersist', array(&$fields));
+ array_unshift($fields, 'visit_first_action_time');
+ array_unshift($fields, 'visit_last_action_time');
+ $fields = array_unique($fields);
+
return $fields;
}
@@ -332,4 +261,9 @@ class Visitor
{
return $this->visitorKnown = $isVisitorKnown;
}
+
+ private function getModel()
+ {
+ return new Model();
+ }
}
diff --git a/core/Translate.php b/core/Translate.php
index 34bd35ba12..65381c0a2d 100644
--- a/core/Translate.php
+++ b/core/Translate.php
@@ -68,7 +68,7 @@ class Translate
private static function loadCoreTranslationFile($language)
{
- if(empty($language)) {
+ if (empty($language)) {
return;
}
$path = PIWIK_INCLUDE_PATH . '/lang/' . $language . '.json';
@@ -174,7 +174,7 @@ class Translate
}
$js = 'var translations = ' . Common::json_encode($clientSideTranslations) . ';';
- $js .= "\n" . 'if(typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }' .
+ $js .= "\n" . 'if (typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }' .
'for(var i in translations) { piwik_translations[i] = translations[i];} ';
return $js;
}
diff --git a/core/Translate/Validate/NoScripts.php b/core/Translate/Validate/NoScripts.php
index 0ee0fc9a57..e7f032ff55 100644
--- a/core/Translate/Validate/NoScripts.php
+++ b/core/Translate/Validate/NoScripts.php
@@ -28,12 +28,14 @@ class NoScripts extends ValidateAbstract
// check if any translation contains restricted script tags
$serializedStrings = serialize($translations);
$invalids = array("<script", 'document.', 'javascript:', 'src=', 'background=', 'onload=');
+
foreach ($invalids as $invalid) {
if (stripos($serializedStrings, $invalid) !== false) {
$this->message = 'script tags restricted for language files';
return false;
}
}
+
return true;
}
}
diff --git a/core/Translate/Writer.php b/core/Translate/Writer.php
index 617b8b9552..c43beca189 100644
--- a/core/Translate/Writer.php
+++ b/core/Translate/Writer.php
@@ -71,7 +71,7 @@ class Writer
protected $filterMessages = array();
const UNFILTERED = 'unfiltered';
- const FILTERED = 'filtered';
+ const FILTERED = 'filtered';
protected $currentState = self::UNFILTERED;
@@ -152,12 +152,14 @@ class Writer
public function getTranslations($lang)
{
$path = $this->getTranslationPathBaseDirectory('lang', $lang);
+
if (!is_readable($path)) {
return array();
}
$data = file_get_contents($path);
$translations = json_decode($data, true);
+
return $translations;
}
diff --git a/core/Twig.php b/core/Twig.php
index b0707350c4..2e0ef95844 100755
--- a/core/Twig.php
+++ b/core/Twig.php
@@ -46,7 +46,7 @@ class Twig
$loaders = array();
//create loader for custom theme to overwrite twig templates
- if($theme && $theme->getPluginName() != \Piwik\Plugin\Manager::DEFAULT_THEME) {
+ if ($theme && $theme->getPluginName() != \Piwik\Plugin\Manager::DEFAULT_THEME) {
$customLoader = $this->getCustomThemeLoader($theme);
if ($customLoader) {
//make it possible to overwrite plugin templates
@@ -187,7 +187,7 @@ class Twig
* @return \Twig_Loader_Filesystem
*/
protected function getCustomThemeLoader(Plugin $theme){
- if(!file_exists(sprintf("%s/plugins/%s/templates/", PIWIK_INCLUDE_PATH, $theme->getPluginName()))){
+ if (!file_exists(sprintf("%s/plugins/%s/templates/", PIWIK_INCLUDE_PATH, $theme->getPluginName()))){
return false;
}
$themeLoader = new Twig_Loader_Filesystem(array(
diff --git a/core/Unzip.php b/core/Unzip.php
index 5b5495c4e4..547a1a84a5 100644
--- a/core/Unzip.php
+++ b/core/Unzip.php
@@ -4,19 +4,17 @@
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
*/
namespace Piwik;
-use Piwik\Unzip\Gzip;
-use Piwik\Unzip\PclZip;
-use Piwik\Unzip\Tar;
-use Piwik\Unzip\ZipArchive;
+use Piwik\Decompress\Gzip;
+use Piwik\Decompress\PclZip;
+use Piwik\Decompress\Tar;
+use Piwik\Decompress\ZipArchive;
/**
- * Unzip wrapper around ZipArchive and PclZip
- *
+ * Factory for Decompress adapters.
*/
class Unzip
{
@@ -25,7 +23,7 @@ class Unzip
*
* @param string $name Name of unarchiver
* @param string $filename Name of .zip archive
- * @return \Piwik\Unzip\UncompressInterface
+ * @return \Piwik\Decompress\DecompressInterface
*/
public static function factory($name, $filename)
{
diff --git a/core/Unzip/Gzip.php b/core/Unzip/Gzip.php
deleted file mode 100755
index 16f08abf0a..0000000000
--- a/core/Unzip/Gzip.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-
-namespace Piwik\Unzip;
-
-/**
- * Unzip implementation for .gz files.
- *
- */
-class Gzip implements UncompressInterface
-{
- /**
- * Name of .gz file.
- *
- * @var string
- */
- private $filename = null;
-
- /**
- * Error string.
- *
- * @var string
- */
- private $error = null;
-
- /**
- * Constructor.
- *
- * @param string $filename Name of .gz file.
- */
- public function __construct($filename)
- {
- $this->filename = $filename;
- }
-
- /**
- * Extracts the contents of the .gz file to $pathExtracted.
- *
- * @param string $pathExtracted Must be file, not directory.
- * @return bool true if successful, false if otherwise.
- */
- public function extract($pathExtracted)
- {
- $file = gzopen($this->filename, 'r');
- if ($file === false) {
- $this->error = "gzopen failed";
- return false;
- }
-
- $output = fopen($pathExtracted, 'w');
- while (!feof($file)) {
- fwrite($output, fread($file, 1024 * 1024));
- }
- fclose($output);
-
- $success = gzclose($file);
- if ($success === false) {
- $this->error = "gzclose failed";
- return false;
- }
-
- return true;
- }
-
- /**
- * Get error status string for the latest error.
- *
- * @return string
- */
- public function errorInfo()
- {
- return $this->error;
- }
-}
-
diff --git a/core/Unzip/PclZip.php b/core/Unzip/PclZip.php
deleted file mode 100644
index b0ce4b3a75..0000000000
--- a/core/Unzip/PclZip.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Unzip;
-
-/**
- * @see libs/PclZip
- */
-require_once PIWIK_INCLUDE_PATH . '/libs/PclZip/pclzip.lib.php';
-
-/**
- * Unzip wrapper around PclZip
- *
- */
-class PclZip implements UncompressInterface
-{
- /**
- * @var PclZip
- */
- private $pclzip;
- /**
- * @var string
- */
- public $filename;
-
- /**
- * Constructor
- *
- * @param string $filename Name of the .zip archive
- */
- public function __construct($filename)
- {
- $this->pclzip = new \PclZip($filename);
- $this->filename = $filename;
- }
-
- /**
- * Extract files from archive to target directory
- *
- * @param string $pathExtracted Absolute path of target directory
- * @return mixed Array of filenames if successful; or 0 if an error occurred
- */
- public function extract($pathExtracted)
- {
- $pathExtracted = str_replace('\\', '/', $pathExtracted);
- $list = $this->pclzip->listContent();
- if (empty($list)) {
- return 0;
- }
-
- foreach ($list as $entry) {
- $filename = str_replace('\\', '/', $entry['stored_filename']);
- $parts = explode('/', $filename);
-
- if (!strncmp($filename, '/', 1) ||
- array_search('..', $parts) !== false ||
- strpos($filename, ':') !== false
- ) {
- return 0;
- }
- }
-
- // PCLZIP_CB_PRE_EXTRACT callback returns 0 to skip, 1 to resume, or 2 to abort
- return $this->pclzip->extract(
- PCLZIP_OPT_PATH, $pathExtracted,
- PCLZIP_OPT_STOP_ON_ERROR,
- PCLZIP_OPT_REPLACE_NEWER,
- PCLZIP_CB_PRE_EXTRACT, function ($p_event, &$p_header) use ($pathExtracted) {
- return strncmp($p_header['filename'], $pathExtracted, strlen($pathExtracted)) ? 0 : 1;
- }
- );
- }
-
- /**
- * Get error status string for the latest error
- *
- * @return string
- */
- public function errorInfo()
- {
- return $this->pclzip->errorInfo(true);
- }
-}
diff --git a/core/Unzip/Tar.php b/core/Unzip/Tar.php
deleted file mode 100755
index 2c87882611..0000000000
--- a/core/Unzip/Tar.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Unzip;
-
-use Archive_Tar;
-
-/**
- * @see libs/Archive_Tar
- */
-require_once PIWIK_INCLUDE_PATH . '/libs/Archive_Tar/Tar.php';
-
-/**
- * Unzip implementation for Archive_Tar PEAR lib.
- *
- */
-class Tar implements UncompressInterface
-{
- /**
- * Archive_Tar instance.
- *
- * @var Archive_Tar
- */
- private $tarArchive = null;
-
- /**
- * Constructor.
- *
- * @param string $filename Path to tar file.
- * @param string|null $compression Either 'gz', 'bz2' or null for no compression.
- */
- public function __construct($filename, $compression = null)
- {
- $this->tarArchive = new Archive_Tar($filename, $compression);
- }
-
- /**
- * Extracts the contents of the tar file to $pathExtracted.
- *
- * @param string $pathExtracted Directory to extract into.
- * @return bool true if successful, false if otherwise.
- */
- public function extract($pathExtracted)
- {
- return $this->tarArchive->extract($pathExtracted);
- }
-
- /**
- * Extracts one file held in a tar archive and returns the deflated file
- * as a string.
- *
- * @param string $inArchivePath Path to file in the tar archive.
- * @return bool true if successful, false if otherwise.
- */
- public function extractInString($inArchivePath)
- {
- return $this->tarArchive->extractInString($inArchivePath);
- }
-
- /**
- * Lists the files held in the tar archive.
- *
- * @return array List of paths describing everything held in the tar archive.
- */
- public function listContent()
- {
- return $this->tarArchive->listContent();
- }
-
- /**
- * Get error status string for the latest error.
- *
- * @return string
- */
- public function errorInfo()
- {
- return $this->tarArchive->error_object->getMessage();
- }
-}
diff --git a/core/Unzip/UncompressInterface.php b/core/Unzip/UncompressInterface.php
deleted file mode 100644
index 072f476bb4..0000000000
--- a/core/Unzip/UncompressInterface.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-
-namespace Piwik\Unzip;
-
-/**
- * Unzip interface
- *
- */
-interface UncompressInterface
-{
- /**
- * Constructor
- *
- * @param string $filename Name of the .zip archive
- */
- public function __construct($filename);
-
- /**
- * Extract files from archive to target directory
- *
- * @param string $pathExtracted Absolute path of target directory
- * @return mixed Array of filenames if successful; or 0 if an error occurred
- */
- public function extract($pathExtracted);
-
- /**
- * Get error status string for the latest error
- *
- * @return string
- */
- public function errorInfo();
-}
diff --git a/core/Unzip/ZipArchive.php b/core/Unzip/ZipArchive.php
deleted file mode 100644
index c8d7edd874..0000000000
--- a/core/Unzip/ZipArchive.php
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Unzip;
-
-use Exception;
-
-/**
- * Unzip wrapper around ZipArchive
- *
- */
-class ZipArchive implements UncompressInterface
-{
- /**
- * @var \ZipArchive
- */
- private $ziparchive;
- /**
- * @var string
- */
- public $filename;
-
- /**
- * Constructor
- *
- * @param string $filename Name of the .zip archive
- * @throws Exception
- */
- public function __construct($filename)
- {
- $this->filename = $filename;
- $this->ziparchive = new \ZipArchive;
- if ($this->ziparchive->open($filename) !== true) {
- throw new Exception('Error opening ' . $filename);
- }
- }
-
- /**
- * Extract files from archive to target directory
- *
- * @param string $pathExtracted Absolute path of target directory
- * @return mixed Array of filenames if successful; or 0 if an error occurred
- */
- public function extract($pathExtracted)
- {
- if (substr($pathExtracted, -1) !== '/') {
- $pathExtracted .= '/';
- }
-
- $fileselector = array();
- $list = array();
- $count = $this->ziparchive->numFiles;
- if ($count === 0) {
- return 0;
- }
-
- for ($i = 0; $i < $count; $i++) {
- $entry = $this->ziparchive->statIndex($i);
-
- $filename = str_replace('\\', '/', $entry['name']);
- $parts = explode('/', $filename);
-
- if (!strncmp($filename, '/', 1) ||
- array_search('..', $parts) !== false ||
- strpos($filename, ':') !== false
- ) {
- return 0;
- }
- $fileselector[] = $entry['name'];
- $list[] = array(
- 'filename' => $pathExtracted . $entry['name'],
- 'stored_filename' => $entry['name'],
- 'size' => $entry['size'],
- 'compressed_size' => $entry['comp_size'],
- 'mtime' => $entry['mtime'],
- 'index' => $i,
- 'crc' => $entry['crc'],
- );
- }
-
- $res = $this->ziparchive->extractTo($pathExtracted, $fileselector);
- if ($res === false)
- return 0;
- return $list;
- }
-
- /**
- * Get error status string for the latest error
- *
- * @return string
- */
- public function errorInfo()
- {
- static $statusStrings = array(
- \ZipArchive::ER_OK => 'No error',
- \ZipArchive::ER_MULTIDISK => 'Multi-disk zip archives not supported',
- \ZipArchive::ER_RENAME => 'Renaming temporary file failed',
- \ZipArchive::ER_CLOSE => 'Closing zip archive failed',
- \ZipArchive::ER_SEEK => 'Seek error',
- \ZipArchive::ER_READ => 'Read error',
- \ZipArchive::ER_WRITE => 'Write error',
- \ZipArchive::ER_CRC => 'CRC error',
- \ZipArchive::ER_ZIPCLOSED => 'Containing zip archive was closed',
- \ZipArchive::ER_NOENT => 'No such file',
- \ZipArchive::ER_EXISTS => 'File already exists',
- \ZipArchive::ER_OPEN => 'Can\'t open file',
- \ZipArchive::ER_TMPOPEN => 'Failure to create temporary file',
- \ZipArchive::ER_ZLIB => 'Zlib error',
- \ZipArchive::ER_MEMORY => 'Malloc failure',
- \ZipArchive::ER_CHANGED => 'Entry has been changed',
- \ZipArchive::ER_COMPNOTSUPP => 'Compression method not supported',
- \ZipArchive::ER_EOF => 'Premature EOF',
- \ZipArchive::ER_INVAL => 'Invalid argument',
- \ZipArchive::ER_NOZIP => 'Not a zip archive',
- \ZipArchive::ER_INTERNAL => 'Internal error',
- \ZipArchive::ER_INCONS => 'Zip archive inconsistent',
- \ZipArchive::ER_REMOVE => 'Can\'t remove file',
- \ZipArchive::ER_DELETED => 'Entry has been deleted',
- );
-
- if (isset($statusStrings[$this->ziparchive->status])) {
- $statusString = $statusStrings[$this->ziparchive->status];
- } else {
- $statusString = 'Unknown status';
- }
- return $statusString . '(' . $this->ziparchive->status . ')';
- }
-}
diff --git a/core/UpdateCheck.php b/core/UpdateCheck.php
index 228ac23a85..43639da9dc 100644
--- a/core/UpdateCheck.php
+++ b/core/UpdateCheck.php
@@ -36,7 +36,7 @@ class UpdateCheck
*/
public static function check($force = false, $interval = null)
{
- if(!self::isAutoUpdateEnabled()) {
+ if (!self::isAutoUpdateEnabled()) {
return;
}
diff --git a/core/Updater.php b/core/Updater.php
index 1527da723a..0069d1b71b 100644
--- a/core/Updater.php
+++ b/core/Updater.php
@@ -355,6 +355,7 @@ class Updater
* @param string $updateSql Update SQL query.
* @param int|false $errorToIgnore A MySQL error code to ignore.
* @param string $file The Update file that's calling this method.
+ * @throws UpdaterErrorException
*/
public static function handleQueryError($e, $updateSql, $errorToIgnore, $file)
{
@@ -371,6 +372,7 @@ class Updater
*
* @param int $error
* @param int|int[] $errorCodesToIgnore
+ * @return boolean
*/
public static function isDbErrorOneOf($error, $errorCodesToIgnore)
{
diff --git a/core/Updates/0.2.34.php b/core/Updates/0.2.34.php
deleted file mode 100644
index 5bdf982d8c..0000000000
--- a/core/Updates/0.2.34.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-
-namespace Piwik\Updates;
-
-use Piwik\Piwik;
-use Piwik\Plugins\SitesManager\API;
-use Piwik\Tracker\Cache;
-use Piwik\Updates;
-
-/**
- */
-class Updates_0_2_34 extends Updates
-{
- static function update()
- {
- // force regeneration of cache files following #648
- Piwik::setUserHasSuperUserAccess();
- $allSiteIds = API::getInstance()->getAllSitesId();
- Cache::regenerateCacheWebsiteAttributes($allSiteIds);
- }
-}
diff --git a/core/Updates/0.6.2.php b/core/Updates/0.6.2.php
deleted file mode 100644
index 0fd9651736..0000000000
--- a/core/Updates/0.6.2.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-
-namespace Piwik\Updates;
-
-use Piwik\Filesystem;
-use Piwik\Piwik;
-use Piwik\Plugins\SitesManager\API;
-use Piwik\Tracker\Cache;
-use Piwik\Updates;
-
-/**
- */
-class Updates_0_6_2 extends Updates
-{
- static function update()
- {
- $obsoleteFiles = array(
- PIWIK_INCLUDE_PATH . '/core/Db/Mysqli.php',
- );
- foreach ($obsoleteFiles as $obsoleteFile) {
- if (file_exists($obsoleteFile)) {
- @unlink($obsoleteFile);
- }
- }
-
- $obsoleteDirectories = array(
- PIWIK_INCLUDE_PATH . '/core/Db/Pdo',
- );
- foreach ($obsoleteDirectories as $dir) {
- if (file_exists($dir)) {
- Filesystem::unlinkRecursive($dir, true);
- }
- }
-
- // force regeneration of cache files
- Piwik::setUserHasSuperUserAccess();
- $allSiteIds = API::getInstance()->getAllSitesId();
- Cache::regenerateCacheWebsiteAttributes($allSiteIds);
- }
-}
diff --git a/core/Updates/2.0-a17.php b/core/Updates/2.0-a17.php
index 2df452c1ab..5d65143596 100644
--- a/core/Updates/2.0-a17.php
+++ b/core/Updates/2.0-a17.php
@@ -35,7 +35,7 @@ class Updates_2_0_a17 extends Updates
}
}
- if(!empty($errors)) {
+ if (!empty($errors)) {
throw new \Exception("Warnings during the update: <br>" . implode("<br>", $errors));
}
}
diff --git a/core/Updates/2.0-b13.php b/core/Updates/2.0-b13.php
index 78cc791566..a5e9546b90 100644
--- a/core/Updates/2.0-b13.php
+++ b/core/Updates/2.0-b13.php
@@ -35,7 +35,7 @@ class Updates_2_0_b13 extends Updates
}
}
- if(!empty($errors)) {
+ if (!empty($errors)) {
throw new \Exception("Warnings during the update: <br>" . implode("<br>", $errors));
}
}
diff --git a/core/Updates/2.0.3-b7.php b/core/Updates/2.0.3-b7.php
index f3fa6a34aa..add119e462 100644
--- a/core/Updates/2.0.3-b7.php
+++ b/core/Updates/2.0.3-b7.php
@@ -56,7 +56,7 @@ class Updates_2_0_3_b7 extends Updates
}
}
- if(!empty($errors)) {
+ if (!empty($errors)) {
throw new \Exception("Warnings during the update: <br>" . implode("<br>", $errors));
}
}
diff --git a/core/Updates/2.1.1-b11.php b/core/Updates/2.1.1-b11.php
index 45b781a1cf..3fa0364e73 100644
--- a/core/Updates/2.1.1-b11.php
+++ b/core/Updates/2.1.1-b11.php
@@ -41,17 +41,14 @@ class Updates_2_1_1_b11 extends Updates
// returning visit segment
foreach ($archiveNumericTables as $table) {
// get archives w/ *._returning
- $sql = "SELECT idarchive, idsite, period, date1, date2
- FROM $table
- WHERE name IN ('" . implode("','", $returningMetrics) . "')
- GROUP BY idarchive";
+ $sql = "SELECT idarchive, idsite, period, date1, date2 FROM $table
+ WHERE name IN ('" . implode("','", $returningMetrics) . "')
+ GROUP BY idarchive";
$idArchivesWithReturning = Db::fetchAll($sql);
// get archives for visitssummary returning visitor segment
- $sql = "SELECT idarchive, idsite, period, date1, date2
- FROM $table
- WHERE name = ?
- GROUP BY idarchive";
+ $sql = "SELECT idarchive, idsite, period, date1, date2 FROM $table
+ WHERE name = ? GROUP BY idarchive";
$visitSummaryReturningSegmentDone = Rules::getDoneFlagArchiveContainsOnePlugin(
new Segment(VisitFrequencyApi::RETURNING_VISITOR_SEGMENT, $idSites = array()), 'VisitsSummary');
$idArchivesWithVisitReturningSegment = Db::fetchAll($sql, array($visitSummaryReturningSegmentDone));
diff --git a/core/Updates/2.4.0-b1.php b/core/Updates/2.4.0-b1.php
index a0f6d64f65..cc0d615d54 100644
--- a/core/Updates/2.4.0-b1.php
+++ b/core/Updates/2.4.0-b1.php
@@ -8,9 +8,6 @@
*/
namespace Piwik\Updates;
-use Faker\Provider\File;
-use Piwik\Filesystem;
-use Piwik\Plugins\Installation\ServerFilesGenerator;
use Piwik\Updates;
class Updates_2_4_0_b1 extends Updates
diff --git a/core/Url.php b/core/Url.php
index 32ea140f20..1ed77f5fa5 100644
--- a/core/Url.php
+++ b/core/Url.php
@@ -10,12 +10,7 @@ namespace Piwik;
use Exception;
-use Piwik\Config;
-use Piwik\Common;
-use Piwik\IP;
-use Piwik\ProxyHttp;
use Piwik\Session;
-use Piwik\UrlHelper;
/**
* Provides URL related helper methods.
@@ -232,18 +227,18 @@ class Url
$trustedHosts = self::getTrustedHosts();
+ // Only punctuation we allow is '[', ']', ':', '.', '_' and '-'
+ $hostLength = strlen($host);
+ if ($hostLength !== strcspn($host, '`~!@#$%^&*()+={}\\|;"\'<>,?/ ')) {
+ return false;
+ }
+
// if no trusted hosts, just assume it's valid
if (empty($trustedHosts)) {
self::saveTrustedHostnameInConfig($host);
return true;
}
- // Only punctuation we allow is '[', ']', ':', '.' and '-'
- $hostLength = strlen($host);
- if ($hostLength !== strcspn($host, '`~!@#$%^&*()_+={}\\|;"\'<>,?/ ')) {
- return false;
- }
-
foreach ($trustedHosts as &$trustedHost) {
$trustedHost = preg_quote($trustedHost);
}
@@ -346,7 +341,7 @@ class Url
$hostHeaders = array();
$config = Config::getInstance()->General;
- if(isset($config['proxy_host_headers'])) {
+ if (isset($config['proxy_host_headers'])) {
$hostHeaders = $config['proxy_host_headers'];
}
@@ -473,6 +468,7 @@ class Url
* Redirects the user to the specified URL.
*
* @param string $url
+ * @throws Exception
* @api
*/
public static function redirectToUrl($url)
@@ -490,7 +486,7 @@ class Url
echo "Invalid URL to redirect to.";
}
- if(Common::isPhpCliMode()) {
+ if (Common::isPhpCliMode()) {
throw new Exception("If you were using a browser, Piwik would redirect you to this URL: $url \n\n");
}
exit;
@@ -501,7 +497,7 @@ class Url
*/
public static function redirectToHttps()
{
- if(ProxyHttp::isHttps()) {
+ if (ProxyHttp::isHttps()) {
return;
}
$url = self::getCurrentUrl();
diff --git a/core/Version.php b/core/Version.php
index 94c6da9cb7..cf2377419e 100644
--- a/core/Version.php
+++ b/core/Version.php
@@ -21,5 +21,5 @@ final class Version
* The current Piwik version.
* @var string
*/
- const VERSION = '2.8.0-b1';
+ const VERSION = '2.9.0-b1';
}
diff --git a/core/View.php b/core/View.php
index bc02e4c792..6200a23113 100644
--- a/core/View.php
+++ b/core/View.php
@@ -9,12 +9,8 @@
namespace Piwik;
use Exception;
-use Piwik\Access;
use Piwik\AssetManager\UIAssetCacheBuster;
-use Piwik\Common;
use Piwik\Plugins\UsersManager\API as APIUsersManager;
-use Piwik\SettingsPiwik;
-use Piwik\Version;
use Piwik\View\ViewInterface;
use Twig_Environment;
diff --git a/core/View/ReportsByDimension.php b/core/View/ReportsByDimension.php
index e1cb058459..dbb59be930 100644
--- a/core/View/ReportsByDimension.php
+++ b/core/View/ReportsByDimension.php
@@ -104,10 +104,10 @@ class ReportsByDimension extends View
// display it initially
$categories = $this->dimensionCategories;
if (!empty($categories)) {
- $firstCategory = reset($categories);
+ $firstCategory = reset($categories);
$firstReportInfo = reset($firstCategory);
- $oldGet = $_GET;
+ $oldGet = $_GET;
$oldPost = $_POST;
foreach ($firstReportInfo['params'] as $key => $value) {
@@ -120,7 +120,7 @@ class ReportsByDimension extends View
$action = $firstReportInfo['params']['action'];
$this->firstReport = FrontController::getInstance()->fetchDispatch($module, $action);
- $_GET = $oldGet;
+ $_GET = $oldGet;
$_POST = $oldPost;
}
diff --git a/core/ViewDataTable/Config.php b/core/ViewDataTable/Config.php
index 2bd748d3e4..1706e4b15f 100644
--- a/core/ViewDataTable/Config.php
+++ b/core/ViewDataTable/Config.php
@@ -598,7 +598,7 @@ class Config
// don't add the related report if it references this report
if ($this->controllerName == $module
&& $this->controllerAction == $action) {
- if(empty($queryParams)) {
+ if (empty($queryParams)) {
return;
}
}
diff --git a/core/ViewDataTable/Manager.php b/core/ViewDataTable/Manager.php
index 19e2335ce1..5d05650657 100644
--- a/core/ViewDataTable/Manager.php
+++ b/core/ViewDataTable/Manager.php
@@ -156,46 +156,7 @@ class Manager
{
$result = array();
- // add normal view icons (eg, normal table, all columns, goals)
- $normalViewIcons = array(
- 'class' => 'tableAllColumnsSwitch',
- 'buttons' => array(),
- );
-
- if ($view->config->show_table) {
- $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable::ID);
- }
-
- if ($view->config->show_table_all_columns) {
- $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable\AllColumns::ID);
- }
-
- if ($view->config->show_goals) {
- $goalButton = static::getFooterIconFor(Goals::ID);
- if (Common::getRequestVar('idGoal', false) == 'ecommerceOrder') {
- $goalButton['icon'] = 'plugins/Morpheus/images/ecommerceOrder.gif';
- }
-
- $normalViewIcons['buttons'][] = $goalButton;
- }
-
- if ($view->config->show_ecommerce) {
- $normalViewIcons['buttons'][] = array(
- 'id' => 'ecommerceOrder',
- 'title' => Piwik::translate('General_EcommerceOrders'),
- 'icon' => 'plugins/Morpheus/images/ecommerceOrder.gif',
- 'text' => Piwik::translate('General_EcommerceOrders')
- );
-
- $normalViewIcons['buttons'][] = array(
- 'id' => 'ecommerceAbandonedCart',
- 'title' => Piwik::translate('General_AbandonedCarts'),
- 'icon' => 'plugins/Morpheus/images/ecommerceAbandonedCart.gif',
- 'text' => Piwik::translate('General_AbandonedCarts')
- );
- }
-
- $normalViewIcons['buttons'] = array_filter($normalViewIcons['buttons']);
+ $normalViewIcons = self::getNormalViewIcons($view);
if (!empty($normalViewIcons['buttons'])) {
$result[] = $normalViewIcons;
@@ -207,25 +168,7 @@ class Manager
'buttons' => array(),
);
- // add graph views
- $graphViewIcons = array(
- 'class' => 'tableGraphViews tableGraphCollapsed',
- 'buttons' => array(),
- );
-
- if ($view->config->show_all_views_icons) {
- if ($view->config->show_bar_chart) {
- $graphViewIcons['buttons'][] = static::getFooterIconFor(Bar::ID);
- }
-
- if ($view->config->show_pie_chart) {
- $graphViewIcons['buttons'][] = static::getFooterIconFor(Pie::ID);
- }
-
- if ($view->config->show_tag_cloud) {
- $graphViewIcons['buttons'][] = static::getFooterIconFor(Cloud::ID);
- }
- }
+ $graphViewIcons = self::getGraphViewIcons($view);
$nonCoreVisualizations = static::getNonCoreViewDataTables();
@@ -331,4 +274,75 @@ class Manager
{
return sprintf('viewDataTableParameters_%s_%s', $login, $controllerAction);
}
+
+ private static function getNormalViewIcons(ViewDataTable $view)
+ {
+ // add normal view icons (eg, normal table, all columns, goals)
+ $normalViewIcons = array(
+ 'class' => 'tableAllColumnsSwitch',
+ 'buttons' => array(),
+ );
+
+ if ($view->config->show_table) {
+ $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable::ID);
+ }
+
+ if ($view->config->show_table_all_columns) {
+ $normalViewIcons['buttons'][] = static::getFooterIconFor(HtmlTable\AllColumns::ID);
+ }
+
+ if ($view->config->show_goals) {
+ $goalButton = static::getFooterIconFor(Goals::ID);
+ if (Common::getRequestVar('idGoal', false) == 'ecommerceOrder') {
+ $goalButton['icon'] = 'plugins/Morpheus/images/ecommerceOrder.gif';
+ }
+
+ $normalViewIcons['buttons'][] = $goalButton;
+ }
+
+ if ($view->config->show_ecommerce) {
+ $normalViewIcons['buttons'][] = array(
+ 'id' => 'ecommerceOrder',
+ 'title' => Piwik::translate('General_EcommerceOrders'),
+ 'icon' => 'plugins/Morpheus/images/ecommerceOrder.gif',
+ 'text' => Piwik::translate('General_EcommerceOrders')
+ );
+
+ $normalViewIcons['buttons'][] = array(
+ 'id' => 'ecommerceAbandonedCart',
+ 'title' => Piwik::translate('General_AbandonedCarts'),
+ 'icon' => 'plugins/Morpheus/images/ecommerceAbandonedCart.gif',
+ 'text' => Piwik::translate('General_AbandonedCarts')
+ );
+ }
+
+ $normalViewIcons['buttons'] = array_filter($normalViewIcons['buttons']);
+
+ return $normalViewIcons;
+ }
+
+ private static function getGraphViewIcons(ViewDataTable $view)
+ {
+ // add graph views
+ $graphViewIcons = array(
+ 'class' => 'tableGraphViews tableGraphCollapsed',
+ 'buttons' => array(),
+ );
+
+ if ($view->config->show_all_views_icons) {
+ if ($view->config->show_bar_chart) {
+ $graphViewIcons['buttons'][] = static::getFooterIconFor(Bar::ID);
+ }
+
+ if ($view->config->show_pie_chart) {
+ $graphViewIcons['buttons'][] = static::getFooterIconFor(Pie::ID);
+ }
+
+ if ($view->config->show_tag_cloud) {
+ $graphViewIcons['buttons'][] = static::getFooterIconFor(Cloud::ID);
+ }
+ }
+
+ return $graphViewIcons;
+ }
}
diff --git a/core/ViewDataTable/Request.php b/core/ViewDataTable/Request.php
index 9493889944..352ce25899 100644
--- a/core/ViewDataTable/Request.php
+++ b/core/ViewDataTable/Request.php
@@ -119,8 +119,8 @@ class Request
if (isset($_GET[$nameVar])) {
return Common::sanitizeInputValue($_GET[$nameVar]);
}
- $default = $this->getDefault($nameVar);
- return $default;
+
+ return $this->getDefault($nameVar);
}
/**
diff --git a/core/Visualization/Sparkline.php b/core/Visualization/Sparkline.php
index 8a7b406342..642c1325cb 100644
--- a/core/Visualization/Sparkline.php
+++ b/core/Visualization/Sparkline.php
@@ -32,6 +32,7 @@ class Sparkline implements ViewInterface
public static $enableSparklineImages = true;
private static $colorNames = array('backgroundColor', 'lineColor', 'minPointColor', 'lastPointColor', 'maxPointColor');
+ private $values = array();
/**
* Width of the sparkline
@@ -60,7 +61,6 @@ class Sparkline implements ViewInterface
*/
public function setHeight($height)
{
-
if (!is_numeric($height) || $height <= 0) {
return;
}
@@ -74,7 +74,6 @@ class Sparkline implements ViewInterface
*/
public function setWidth($width)
{
-
if (!is_numeric($width) || $width <= 0) {
return;
}
@@ -110,7 +109,9 @@ class Sparkline implements ViewInterface
$min = $max = $last = null;
$i = 0;
- $toRemove = array('%', str_replace('%s', '', Piwik::translate('General_Seconds')));
+ $seconds = Piwik::translate('General_Seconds');
+ $toRemove = array('%', str_replace('%s', '', $seconds));
+
foreach ($this->values as $value) {
// 50% and 50s should be plotted as 50
$value = str_replace($toRemove, '', $value);
@@ -158,6 +159,7 @@ class Sparkline implements ViewInterface
private function setSparklineColors($sparkline)
{
$colors = Common::getRequestVar('colors', false, 'json');
+
if (empty($colors)) { // quick fix so row evolution sparklines will have color in widgetize's iframes
$colors = array(
'backgroundColor' => '#ffffff',
diff --git a/core/dispatch.php b/core/dispatch.php
index f18b73f0bd..133f26f355 100644
--- a/core/dispatch.php
+++ b/core/dispatch.php
@@ -24,7 +24,7 @@ if (!defined('PIWIK_ENABLE_ERROR_HANDLER') || PIWIK_ENABLE_ERROR_HANDLER) {
FrontController::setUpSafeMode();
-if(!defined('PIWIK_ENABLE_DISPATCH')) {
+if (!defined('PIWIK_ENABLE_DISPATCH')) {
define('PIWIK_ENABLE_DISPATCH', true);
}
@@ -33,7 +33,7 @@ if (PIWIK_ENABLE_DISPATCH) {
$controller->init();
$response = $controller->dispatch();
- if(is_array($response)) {
+ if (is_array($response)) {
var_export($response);
} elseif (!is_null($response)) {
echo $response;
diff --git a/core/testMinimumPhpVersion.php b/core/testMinimumPhpVersion.php
index bce404c9e6..3dd3defd3e 100644
--- a/core/testMinimumPhpVersion.php
+++ b/core/testMinimumPhpVersion.php
@@ -129,7 +129,7 @@ if (!function_exists('Piwik_ExitWithMessage')) {
$message = str_replace("\t", "", $message);
$message = strip_tags($message);
- if($isCli) {
+ if ($isCli) {
echo $message;
} else {
echo $headerPage . $content . $footerPage;
diff --git a/libs/Archive_Tar/Tar.php b/libs/Archive_Tar/Tar.php
deleted file mode 100755
index 167a485b50..0000000000
--- a/libs/Archive_Tar/Tar.php
+++ /dev/null
@@ -1,1993 +0,0 @@
-<?php
-/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
-
-/**
- * File::CSV
- *
- * PHP versions 4 and 5
- *
- * Copyright (c) 1997-2008,
- * Vincent Blavet <vincent@phpconcept.net>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * @category File_Formats
- * @package Archive_Tar
- * @author Vincent Blavet <vincent@phpconcept.net>
- * @copyright 1997-2010 The Authors
- * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
- * @version CVS: $Id$
- * @link http://pear.php.net/package/Archive_Tar
- */
-
-require_once __DIR__ . "/../PEAR.php";
-
-define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
-define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
-
-/**
-* Creates a (compressed) Tar archive
-*
-* @package Archive_Tar
-* @author Vincent Blavet <vincent@phpconcept.net>
-* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
-* @version $Revision$
-*/
-class Archive_Tar extends PEAR
-{
- /**
- * @var string Name of the Tar
- */
- var $_tarname='';
-
- /**
- * @var boolean if true, the Tar file will be gzipped
- */
- var $_compress=false;
-
- /**
- * @var string Type of compression : 'none', 'gz' or 'bz2'
- */
- var $_compress_type='none';
-
- /**
- * @var string Explode separator
- */
- var $_separator=' ';
-
- /**
- * @var file descriptor
- */
- var $_file=0;
-
- /**
- * @var string Local Tar name of a remote Tar (http:// or ftp://)
- */
- var $_temp_tarname='';
-
- /**
- * @var string regular expression for ignoring files or directories
- */
- var $_ignore_regexp='';
-
- /**
- * @var object PEAR_Error object
- */
- var $error_object=null;
-
- // {{{ constructor
- /**
- * Archive_Tar Class constructor. This flavour of the constructor only
- * declare a new Archive_Tar object, identifying it by the name of the
- * tar file.
- * If the compress argument is set the tar will be read or created as a
- * gzip or bz2 compressed TAR file.
- *
- * @param string $p_tarname The name of the tar archive to create
- * @param string $p_compress can be null, 'gz' or 'bz2'. This
- * parameter indicates if gzip or bz2 compression
- * is required. For compatibility reason the
- * boolean value 'true' means 'gz'.
- *
- * @access public
- */
- function Archive_Tar($p_tarname, $p_compress = null)
- {
- $this->PEAR();
- $this->_compress = false;
- $this->_compress_type = 'none';
- if (($p_compress === null) || ($p_compress == '')) {
- if (@file_exists($p_tarname)) {
- if ($fp = @fopen($p_tarname, "rb")) {
- // look for gzip magic cookie
- $data = fread($fp, 2);
- fclose($fp);
- if ($data == "\37\213") {
- $this->_compress = true;
- $this->_compress_type = 'gz';
- // No sure it's enought for a magic code ....
- } elseif ($data == "BZ") {
- $this->_compress = true;
- $this->_compress_type = 'bz2';
- }
- }
- } else {
- // probably a remote file or some file accessible
- // through a stream interface
- if (substr($p_tarname, -2) == 'gz') {
- $this->_compress = true;
- $this->_compress_type = 'gz';
- } elseif ((substr($p_tarname, -3) == 'bz2') ||
- (substr($p_tarname, -2) == 'bz')) {
- $this->_compress = true;
- $this->_compress_type = 'bz2';
- }
- }
- } else {
- if (($p_compress === true) || ($p_compress == 'gz')) {
- $this->_compress = true;
- $this->_compress_type = 'gz';
- } else if ($p_compress == 'bz2') {
- $this->_compress = true;
- $this->_compress_type = 'bz2';
- } else {
- $this->_error("Unsupported compression type '$p_compress'\n".
- "Supported types are 'gz' and 'bz2'.\n");
- return false;
- }
- }
- $this->_tarname = $p_tarname;
- if ($this->_compress) { // assert zlib or bz2 extension support
- if ($this->_compress_type == 'gz')
- $extname = 'zlib';
- else if ($this->_compress_type == 'bz2')
- $extname = 'bz2';
-
- if (!extension_loaded($extname)) {
- PEAR::loadExtension($extname);
- }
- if (!extension_loaded($extname)) {
- $this->_error("The extension '$extname' couldn't be found.\n".
- "Please make sure your version of PHP was built ".
- "with '$extname' support.\n");
- return false;
- }
- }
- }
- // }}}
-
- // {{{ destructor
- function _Archive_Tar()
- {
- $this->_close();
- // ----- Look for a local copy to delete
- if ($this->_temp_tarname != '')
- @unlink($this->_temp_tarname);
- $this->_PEAR();
- }
- // }}}
-
- // {{{ create()
- /**
- * This method creates the archive file and add the files / directories
- * that are listed in $p_filelist.
- * If a file with the same name exist and is writable, it is replaced
- * by the new tar.
- * The method return false and a PEAR error text.
- * The $p_filelist parameter can be an array of string, each string
- * representing a filename or a directory name with their path if
- * needed. It can also be a single string with names separated by a
- * single blank.
- * For each directory added in the archive, the files and
- * sub-directories are also added.
- * See also createModify() method for more details.
- *
- * @param array $p_filelist An array of filenames and directory names, or a
- * single string with names separated by a single
- * blank space.
- *
- * @return true on success, false on error.
- * @see createModify()
- * @access public
- */
- function create($p_filelist)
- {
- return $this->createModify($p_filelist, '', '');
- }
- // }}}
-
- // {{{ add()
- /**
- * This method add the files / directories that are listed in $p_filelist in
- * the archive. If the archive does not exist it is created.
- * The method return false and a PEAR error text.
- * The files and directories listed are only added at the end of the archive,
- * even if a file with the same name is already archived.
- * See also createModify() method for more details.
- *
- * @param array $p_filelist An array of filenames and directory names, or a
- * single string with names separated by a single
- * blank space.
- *
- * @return true on success, false on error.
- * @see createModify()
- * @access public
- */
- function add($p_filelist)
- {
- return $this->addModify($p_filelist, '', '');
- }
- // }}}
-
- // {{{ extract()
- function extract($p_path='', $p_preserve=false)
- {
- return $this->extractModify($p_path, '', $p_preserve);
- }
- // }}}
-
- // {{{ listContent()
- function listContent()
- {
- $v_list_detail = array();
-
- if ($this->_openRead()) {
- if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
- unset($v_list_detail);
- $v_list_detail = 0;
- }
- $this->_close();
- }
-
- return $v_list_detail;
- }
- // }}}
-
- // {{{ createModify()
- /**
- * This method creates the archive file and add the files / directories
- * that are listed in $p_filelist.
- * If the file already exists and is writable, it is replaced by the
- * new tar. It is a create and not an add. If the file exists and is
- * read-only or is a directory it is not replaced. The method return
- * false and a PEAR error text.
- * The $p_filelist parameter can be an array of string, each string
- * representing a filename or a directory name with their path if
- * needed. It can also be a single string with names separated by a
- * single blank.
- * The path indicated in $p_remove_dir will be removed from the
- * memorized path of each file / directory listed when this path
- * exists. By default nothing is removed (empty path '')
- * The path indicated in $p_add_dir will be added at the beginning of
- * the memorized path of each file / directory listed. However it can
- * be set to empty ''. The adding of a path is done after the removing
- * of path.
- * The path add/remove ability enables the user to prepare an archive
- * for extraction in a different path than the origin files are.
- * See also addModify() method for file adding properties.
- *
- * @param array $p_filelist An array of filenames and directory names,
- * or a single string with names separated by
- * a single blank space.
- * @param string $p_add_dir A string which contains a path to be added
- * to the memorized path of each element in
- * the list.
- * @param string $p_remove_dir A string which contains a path to be
- * removed from the memorized path of each
- * element in the list, when relevant.
- *
- * @return boolean true on success, false on error.
- * @access public
- * @see addModify()
- */
- function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
- {
- $v_result = true;
-
- if (!$this->_openWrite())
- return false;
-
- if ($p_filelist != '') {
- if (is_array($p_filelist))
- $v_list = $p_filelist;
- elseif (is_string($p_filelist))
- $v_list = explode($this->_separator, $p_filelist);
- else {
- $this->_cleanFile();
- $this->_error('Invalid file list');
- return false;
- }
-
- $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
- }
-
- if ($v_result) {
- $this->_writeFooter();
- $this->_close();
- } else
- $this->_cleanFile();
-
- return $v_result;
- }
- // }}}
-
- // {{{ addModify()
- /**
- * This method add the files / directories listed in $p_filelist at the
- * end of the existing archive. If the archive does not yet exists it
- * is created.
- * The $p_filelist parameter can be an array of string, each string
- * representing a filename or a directory name with their path if
- * needed. It can also be a single string with names separated by a
- * single blank.
- * The path indicated in $p_remove_dir will be removed from the
- * memorized path of each file / directory listed when this path
- * exists. By default nothing is removed (empty path '')
- * The path indicated in $p_add_dir will be added at the beginning of
- * the memorized path of each file / directory listed. However it can
- * be set to empty ''. The adding of a path is done after the removing
- * of path.
- * The path add/remove ability enables the user to prepare an archive
- * for extraction in a different path than the origin files are.
- * If a file/dir is already in the archive it will only be added at the
- * end of the archive. There is no update of the existing archived
- * file/dir. However while extracting the archive, the last file will
- * replace the first one. This results in a none optimization of the
- * archive size.
- * If a file/dir does not exist the file/dir is ignored. However an
- * error text is send to PEAR error.
- * If a file/dir is not readable the file/dir is ignored. However an
- * error text is send to PEAR error.
- *
- * @param array $p_filelist An array of filenames and directory
- * names, or a single string with names
- * separated by a single blank space.
- * @param string $p_add_dir A string which contains a path to be
- * added to the memorized path of each
- * element in the list.
- * @param string $p_remove_dir A string which contains a path to be
- * removed from the memorized path of
- * each element in the list, when
- * relevant.
- *
- * @return true on success, false on error.
- * @access public
- */
- function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
- {
- $v_result = true;
-
- if (!$this->_isArchive())
- $v_result = $this->createModify($p_filelist, $p_add_dir,
- $p_remove_dir);
- else {
- if (is_array($p_filelist))
- $v_list = $p_filelist;
- elseif (is_string($p_filelist))
- $v_list = explode($this->_separator, $p_filelist);
- else {
- $this->_error('Invalid file list');
- return false;
- }
-
- $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
- }
-
- return $v_result;
- }
- // }}}
-
- // {{{ addString()
- /**
- * This method add a single string as a file at the
- * end of the existing archive. If the archive does not yet exists it
- * is created.
- *
- * @param string $p_filename A string which contains the full
- * filename path that will be associated
- * with the string.
- * @param string $p_string The content of the file added in
- * the archive.
- * @param int $p_datetime A custom date/time (unix timestamp)
- * for the file (optional).
- *
- * @return true on success, false on error.
- * @access public
- */
- function addString($p_filename, $p_string, $p_datetime = false)
- {
- $v_result = true;
-
- if (!$this->_isArchive()) {
- if (!$this->_openWrite()) {
- return false;
- }
- $this->_close();
- }
-
- if (!$this->_openAppend())
- return false;
-
- // Need to check the get back to the temporary file ? ....
- $v_result = $this->_addString($p_filename, $p_string, $p_datetime);
-
- $this->_writeFooter();
-
- $this->_close();
-
- return $v_result;
- }
- // }}}
-
- // {{{ extractModify()
- /**
- * This method extract all the content of the archive in the directory
- * indicated by $p_path. When relevant the memorized path of the
- * files/dir can be modified by removing the $p_remove_path path at the
- * beginning of the file/dir path.
- * While extracting a file, if the directory path does not exists it is
- * created.
- * While extracting a file, if the file already exists it is replaced
- * without looking for last modification date.
- * While extracting a file, if the file already exists and is write
- * protected, the extraction is aborted.
- * While extracting a file, if a directory with the same name already
- * exists, the extraction is aborted.
- * While extracting a directory, if a file with the same name already
- * exists, the extraction is aborted.
- * While extracting a file/directory if the destination directory exist
- * and is write protected, or does not exist but can not be created,
- * the extraction is aborted.
- * If after extraction an extracted file does not show the correct
- * stored file size, the extraction is aborted.
- * When the extraction is aborted, a PEAR error text is set and false
- * is returned. However the result can be a partial extraction that may
- * need to be manually cleaned.
- *
- * @param string $p_path The path of the directory where the
- * files/dir need to by extracted.
- * @param string $p_remove_path Part of the memorized path that can be
- * removed if present at the beginning of
- * the file/dir path.
- * @param boolean $p_preserve Preserve user/group ownership of files
- *
- * @return boolean true on success, false on error.
- * @access public
- * @see extractList()
- */
- function extractModify($p_path, $p_remove_path, $p_preserve=false)
- {
- $v_result = true;
- $v_list_detail = array();
-
- if ($v_result = $this->_openRead()) {
- $v_result = $this->_extractList($p_path, $v_list_detail,
- "complete", 0, $p_remove_path, $p_preserve);
- $this->_close();
- }
-
- return $v_result;
- }
- // }}}
-
- // {{{ extractInString()
- /**
- * This method extract from the archive one file identified by $p_filename.
- * The return value is a string with the file content, or NULL on error.
- *
- * @param string $p_filename The path of the file to extract in a string.
- *
- * @return a string with the file content or NULL.
- * @access public
- */
- function extractInString($p_filename)
- {
- if ($this->_openRead()) {
- $v_result = $this->_extractInString($p_filename);
- $this->_close();
- } else {
- $v_result = null;
- }
-
- return $v_result;
- }
- // }}}
-
- // {{{ extractList()
- /**
- * This method extract from the archive only the files indicated in the
- * $p_filelist. These files are extracted in the current directory or
- * in the directory indicated by the optional $p_path parameter.
- * If indicated the $p_remove_path can be used in the same way as it is
- * used in extractModify() method.
- *
- * @param array $p_filelist An array of filenames and directory names,
- * or a single string with names separated
- * by a single blank space.
- * @param string $p_path The path of the directory where the
- * files/dir need to by extracted.
- * @param string $p_remove_path Part of the memorized path that can be
- * removed if present at the beginning of
- * the file/dir path.
- * @param boolean $p_preserve Preserve user/group ownership of files
- *
- * @return true on success, false on error.
- * @access public
- * @see extractModify()
- */
- function extractList($p_filelist, $p_path='', $p_remove_path='', $p_preserve=false)
- {
- $v_result = true;
- $v_list_detail = array();
-
- if (is_array($p_filelist))
- $v_list = $p_filelist;
- elseif (is_string($p_filelist))
- $v_list = explode($this->_separator, $p_filelist);
- else {
- $this->_error('Invalid string list');
- return false;
- }
-
- if ($v_result = $this->_openRead()) {
- $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
- $v_list, $p_remove_path, $p_preserve);
- $this->_close();
- }
-
- return $v_result;
- }
- // }}}
-
- // {{{ setAttribute()
- /**
- * This method set specific attributes of the archive. It uses a variable
- * list of parameters, in the format attribute code + attribute values :
- * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
- *
- * @param mixed $argv variable list of attributes and values
- *
- * @return true on success, false on error.
- * @access public
- */
- function setAttribute()
- {
- $v_result = true;
-
- // ----- Get the number of variable list of arguments
- if (($v_size = func_num_args()) == 0) {
- return true;
- }
-
- // ----- Get the arguments
- $v_att_list = &func_get_args();
-
- // ----- Read the attributes
- $i=0;
- while ($i<$v_size) {
-
- // ----- Look for next option
- switch ($v_att_list[$i]) {
- // ----- Look for options that request a string value
- case ARCHIVE_TAR_ATT_SEPARATOR :
- // ----- Check the number of parameters
- if (($i+1) >= $v_size) {
- $this->_error('Invalid number of parameters for '
- .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
- return false;
- }
-
- // ----- Get the value
- $this->_separator = $v_att_list[$i+1];
- $i++;
- break;
-
- default :
- $this->_error('Unknow attribute code '.$v_att_list[$i].'');
- return false;
- }
-
- // ----- Next attribute
- $i++;
- }
-
- return $v_result;
- }
- // }}}
-
- // {{{ setIgnoreRegexp()
- /**
- * This method sets the regular expression for ignoring files and directories
- * at import, for example:
- * $arch->setIgnoreRegexp("#CVS|\.svn#");
- *
- * @param string $regexp regular expression defining which files or directories to ignore
- *
- * @access public
- */
- function setIgnoreRegexp($regexp)
- {
- $this->_ignore_regexp = $regexp;
- }
- // }}}
-
- // {{{ setIgnoreList()
- /**
- * This method sets the regular expression for ignoring all files and directories
- * matching the filenames in the array list at import, for example:
- * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool'));
- *
- * @param array $list a list of file or directory names to ignore
- *
- * @access public
- */
- function setIgnoreList($list)
- {
- $regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
- $regexp = '#/'.join('$|/', $list).'#';
- $this->setIgnoreRegexp($regexp);
- }
- // }}}
-
- // {{{ _error()
- function _error($p_message)
- {
- $this->error_object = &$this->raiseError($p_message);
- }
- // }}}
-
- // {{{ _warning()
- function _warning($p_message)
- {
- $this->error_object = &$this->raiseError($p_message);
- }
- // }}}
-
- // {{{ _isArchive()
- function _isArchive($p_filename=null)
- {
- if ($p_filename == null) {
- $p_filename = $this->_tarname;
- }
- clearstatcache();
- return @is_file($p_filename) && !@is_link($p_filename);
- }
- // }}}
-
- // {{{ _openWrite()
- function _openWrite()
- {
- if ($this->_compress_type == 'gz' && function_exists('gzopen'))
- $this->_file = @gzopen($this->_tarname, "wb9");
- else if ($this->_compress_type == 'bz2' && function_exists('bzopen'))
- $this->_file = @bzopen($this->_tarname, "w");
- else if ($this->_compress_type == 'none')
- $this->_file = @fopen($this->_tarname, "wb");
- else {
- $this->_error('Unknown or missing compression type ('
- .$this->_compress_type.')');
- return false;
- }
-
- if ($this->_file == 0) {
- $this->_error('Unable to open in write mode \''
- .$this->_tarname.'\'');
- return false;
- }
-
- return true;
- }
- // }}}
-
- // {{{ _openRead()
- function _openRead()
- {
- if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
-
- // ----- Look if a local copy need to be done
- if ($this->_temp_tarname == '') {
- $this->_temp_tarname = uniqid('tar').'.tmp';
- if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
- $this->_error('Unable to open in read mode \''
- .$this->_tarname.'\'');
- $this->_temp_tarname = '';
- return false;
- }
- if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
- $this->_error('Unable to open in write mode \''
- .$this->_temp_tarname.'\'');
- $this->_temp_tarname = '';
- return false;
- }
- while ($v_data = @fread($v_file_from, 1024))
- @fwrite($v_file_to, $v_data);
- @fclose($v_file_from);
- @fclose($v_file_to);
- }
-
- // ----- File to open if the local copy
- $v_filename = $this->_temp_tarname;
-
- } else
- // ----- File to open if the normal Tar file
- $v_filename = $this->_tarname;
-
- if ($this->_compress_type == 'gz' && function_exists('gzopen'))
- $this->_file = @gzopen($v_filename, "rb");
- else if ($this->_compress_type == 'bz2' && function_exists('bzopen'))
- $this->_file = @bzopen($v_filename, "r");
- else if ($this->_compress_type == 'none')
- $this->_file = @fopen($v_filename, "rb");
- else {
- $this->_error('Unknown or missing compression type ('
- .$this->_compress_type.')');
- return false;
- }
-
- if ($this->_file == 0) {
- $this->_error('Unable to open in read mode \''.$v_filename.'\'');
- return false;
- }
-
- return true;
- }
- // }}}
-
- // {{{ _openReadWrite()
- function _openReadWrite()
- {
- if ($this->_compress_type == 'gz')
- $this->_file = @gzopen($this->_tarname, "r+b");
- else if ($this->_compress_type == 'bz2') {
- $this->_error('Unable to open bz2 in read/write mode \''
- .$this->_tarname.'\' (limitation of bz2 extension)');
- return false;
- } else if ($this->_compress_type == 'none')
- $this->_file = @fopen($this->_tarname, "r+b");
- else {
- $this->_error('Unknown or missing compression type ('
- .$this->_compress_type.')');
- return false;
- }
-
- if ($this->_file == 0) {
- $this->_error('Unable to open in read/write mode \''
- .$this->_tarname.'\'');
- return false;
- }
-
- return true;
- }
- // }}}
-
- // {{{ _close()
- function _close()
- {
- //if (isset($this->_file)) {
- if (is_resource($this->_file)) {
- if ($this->_compress_type == 'gz')
- @gzclose($this->_file);
- else if ($this->_compress_type == 'bz2')
- @bzclose($this->_file);
- else if ($this->_compress_type == 'none')
- @fclose($this->_file);
- else
- $this->_error('Unknown or missing compression type ('
- .$this->_compress_type.')');
-
- $this->_file = 0;
- }
-
- // ----- Look if a local copy need to be erase
- // Note that it might be interesting to keep the url for a time : ToDo
- if ($this->_temp_tarname != '') {
- @unlink($this->_temp_tarname);
- $this->_temp_tarname = '';
- }
-
- return true;
- }
- // }}}
-
- // {{{ _cleanFile()
- function _cleanFile()
- {
- $this->_close();
-
- // ----- Look for a local copy
- if ($this->_temp_tarname != '') {
- // ----- Remove the local copy but not the remote tarname
- @unlink($this->_temp_tarname);
- $this->_temp_tarname = '';
- } else {
- // ----- Remove the local tarname file
- @unlink($this->_tarname);
- }
- $this->_tarname = '';
-
- return true;
- }
- // }}}
-
- // {{{ _writeBlock()
- function _writeBlock($p_binary_data, $p_len=null)
- {
- if (is_resource($this->_file)) {
- if ($p_len === null) {
- if ($this->_compress_type == 'gz')
- @gzputs($this->_file, $p_binary_data);
- else if ($this->_compress_type == 'bz2')
- @bzwrite($this->_file, $p_binary_data);
- else if ($this->_compress_type == 'none')
- @fputs($this->_file, $p_binary_data);
- else
- $this->_error('Unknown or missing compression type ('
- .$this->_compress_type.')');
- } else {
- if ($this->_compress_type == 'gz')
- @gzputs($this->_file, $p_binary_data, $p_len);
- else if ($this->_compress_type == 'bz2')
- @bzwrite($this->_file, $p_binary_data, $p_len);
- else if ($this->_compress_type == 'none')
- @fputs($this->_file, $p_binary_data, $p_len);
- else
- $this->_error('Unknown or missing compression type ('
- .$this->_compress_type.')');
-
- }
- }
- return true;
- }
- // }}}
-
- // {{{ _readBlock()
- function _readBlock()
- {
- $v_block = null;
- if (is_resource($this->_file)) {
- if ($this->_compress_type == 'gz')
- $v_block = @gzread($this->_file, 512);
- else if ($this->_compress_type == 'bz2')
- $v_block = @bzread($this->_file, 512);
- else if ($this->_compress_type == 'none')
- $v_block = @fread($this->_file, 512);
- else
- $this->_error('Unknown or missing compression type ('
- .$this->_compress_type.')');
- }
- return $v_block;
- }
- // }}}
-
- // {{{ _jumpBlock()
- function _jumpBlock($p_len=null)
- {
- if (is_resource($this->_file)) {
- if ($p_len === null)
- $p_len = 1;
-
- if ($this->_compress_type == 'gz') {
- @gzseek($this->_file, gztell($this->_file)+($p_len*512));
- }
- else if ($this->_compress_type == 'bz2') {
- // ----- Replace missing bztell() and bzseek()
- for ($i=0; $i<$p_len; $i++)
- $this->_readBlock();
- } else if ($this->_compress_type == 'none')
- @fseek($this->_file, $p_len*512, SEEK_CUR);
- else
- $this->_error('Unknown or missing compression type ('
- .$this->_compress_type.')');
-
- }
- return true;
- }
- // }}}
-
- // {{{ _writeFooter()
- function _writeFooter()
- {
- if (is_resource($this->_file)) {
- // ----- Write the last 0 filled block for end of archive
- $v_binary_data = pack('a1024', '');
- $this->_writeBlock($v_binary_data);
- }
- return true;
- }
- // }}}
-
- // {{{ _addList()
- function _addList($p_list, $p_add_dir, $p_remove_dir)
- {
- $v_result=true;
- $v_header = array();
-
- // ----- Remove potential windows directory separator
- $p_add_dir = $this->_translateWinPath($p_add_dir);
- $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
-
- if (!$this->_file) {
- $this->_error('Invalid file descriptor');
- return false;
- }
-
- if (sizeof($p_list) == 0)
- return true;
-
- foreach ($p_list as $v_filename) {
- if (!$v_result) {
- break;
- }
-
- // ----- Skip the current tar name
- if ($v_filename == $this->_tarname)
- continue;
-
- if ($v_filename == '')
- continue;
-
- // ----- ignore files and directories matching the ignore regular expression
- if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/'.$v_filename)) {
- $this->_warning("File '$v_filename' ignored");
- continue;
- }
-
- if (!file_exists($v_filename) && !is_link($v_filename)) {
- $this->_warning("File '$v_filename' does not exist");
- continue;
- }
-
- // ----- Add the file or directory header
- if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
- return false;
-
- if (@is_dir($v_filename) && !@is_link($v_filename)) {
- if (!($p_hdir = opendir($v_filename))) {
- $this->_warning("Directory '$v_filename' can not be read");
- continue;
- }
- while (false !== ($p_hitem = readdir($p_hdir))) {
- if (($p_hitem != '.') && ($p_hitem != '..')) {
- if ($v_filename != ".")
- $p_temp_list[0] = $v_filename.'/'.$p_hitem;
- else
- $p_temp_list[0] = $p_hitem;
-
- $v_result = $this->_addList($p_temp_list,
- $p_add_dir,
- $p_remove_dir);
- }
- }
-
- unset($p_temp_list);
- unset($p_hdir);
- unset($p_hitem);
- }
- }
-
- return $v_result;
- }
- // }}}
-
- // {{{ _addFile()
- function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
- {
- if (!$this->_file) {
- $this->_error('Invalid file descriptor');
- return false;
- }
-
- if ($p_filename == '') {
- $this->_error('Invalid file name');
- return false;
- }
-
- // ----- Calculate the stored filename
- $p_filename = $this->_translateWinPath($p_filename, false);;
- $v_stored_filename = $p_filename;
- if (strcmp($p_filename, $p_remove_dir) == 0) {
- return true;
- }
- if ($p_remove_dir != '') {
- if (substr($p_remove_dir, -1) != '/')
- $p_remove_dir .= '/';
-
- if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
- $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
- }
- $v_stored_filename = $this->_translateWinPath($v_stored_filename);
- if ($p_add_dir != '') {
- if (substr($p_add_dir, -1) == '/')
- $v_stored_filename = $p_add_dir.$v_stored_filename;
- else
- $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
- }
-
- $v_stored_filename = $this->_pathReduction($v_stored_filename);
-
- if ($this->_isArchive($p_filename)) {
- if (($v_file = @fopen($p_filename, "rb")) == 0) {
- $this->_warning("Unable to open file '".$p_filename
- ."' in binary read mode");
- return true;
- }
-
- if (!$this->_writeHeader($p_filename, $v_stored_filename))
- return false;
-
- while (($v_buffer = fread($v_file, 512)) != '') {
- $v_binary_data = pack("a512", "$v_buffer");
- $this->_writeBlock($v_binary_data);
- }
-
- fclose($v_file);
-
- } else {
- // ----- Only header for dir
- if (!$this->_writeHeader($p_filename, $v_stored_filename))
- return false;
- }
-
- return true;
- }
- // }}}
-
- // {{{ _addString()
- function _addString($p_filename, $p_string, $p_datetime = false)
- {
- if (!$this->_file) {
- $this->_error('Invalid file descriptor');
- return false;
- }
-
- if ($p_filename == '') {
- $this->_error('Invalid file name');
- return false;
- }
-
- // ----- Calculate the stored filename
- $p_filename = $this->_translateWinPath($p_filename, false);;
-
- // ----- If datetime is not specified, set current time
- if ($p_datetime === false) {
- $p_datetime = time();
- }
-
- if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
- $p_datetime, 384, "", 0, 0))
- return false;
-
- $i=0;
- while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
- $v_binary_data = pack("a512", $v_buffer);
- $this->_writeBlock($v_binary_data);
- }
-
- return true;
- }
- // }}}
-
- // {{{ _writeHeader()
- function _writeHeader($p_filename, $p_stored_filename)
- {
- if ($p_stored_filename == '')
- $p_stored_filename = $p_filename;
- $v_reduce_filename = $this->_pathReduction($p_stored_filename);
-
- if (strlen($v_reduce_filename) > 99) {
- if (!$this->_writeLongHeader($v_reduce_filename))
- return false;
- }
-
- $v_info = lstat($p_filename);
- $v_uid = sprintf("%07s", DecOct($v_info[4]));
- $v_gid = sprintf("%07s", DecOct($v_info[5]));
- $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
-
- $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
-
- $v_linkname = '';
-
- if (@is_link($p_filename)) {
- $v_typeflag = '2';
- $v_linkname = readlink($p_filename);
- $v_size = sprintf("%011s", DecOct(0));
- } elseif (@is_dir($p_filename)) {
- $v_typeflag = "5";
- $v_size = sprintf("%011s", DecOct(0));
- } else {
- $v_typeflag = '0';
- clearstatcache();
- $v_size = sprintf("%011s", DecOct($v_info['size']));
- }
-
- $v_magic = 'ustar ';
-
- $v_version = ' ';
-
- if (function_exists('posix_getpwuid'))
- {
- $userinfo = posix_getpwuid($v_info[4]);
- $groupinfo = posix_getgrgid($v_info[5]);
-
- $v_uname = $userinfo['name'];
- $v_gname = $groupinfo['name'];
- }
- else
- {
- $v_uname = '';
- $v_gname = '';
- }
-
- $v_devmajor = '';
-
- $v_devminor = '';
-
- $v_prefix = '';
-
- $v_binary_data_first = pack("a100a8a8a8a12a12",
- $v_reduce_filename, $v_perms, $v_uid,
- $v_gid, $v_size, $v_mtime);
- $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
- $v_typeflag, $v_linkname, $v_magic,
- $v_version, $v_uname, $v_gname,
- $v_devmajor, $v_devminor, $v_prefix, '');
-
- // ----- Calculate the checksum
- $v_checksum = 0;
- // ..... First part of the header
- for ($i=0; $i<148; $i++)
- $v_checksum += ord(substr($v_binary_data_first,$i,1));
- // ..... Ignore the checksum value and replace it by ' ' (space)
- for ($i=148; $i<156; $i++)
- $v_checksum += ord(' ');
- // ..... Last part of the header
- for ($i=156, $j=0; $i<512; $i++, $j++)
- $v_checksum += ord(substr($v_binary_data_last,$j,1));
-
- // ----- Write the first 148 bytes of the header in the archive
- $this->_writeBlock($v_binary_data_first, 148);
-
- // ----- Write the calculated checksum
- $v_checksum = sprintf("%06s ", DecOct($v_checksum));
- $v_binary_data = pack("a8", $v_checksum);
- $this->_writeBlock($v_binary_data, 8);
-
- // ----- Write the last 356 bytes of the header in the archive
- $this->_writeBlock($v_binary_data_last, 356);
-
- return true;
- }
- // }}}
-
- // {{{ _writeHeaderBlock()
- function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
- $p_type='', $p_uid=0, $p_gid=0)
- {
- $p_filename = $this->_pathReduction($p_filename);
-
- if (strlen($p_filename) > 99) {
- if (!$this->_writeLongHeader($p_filename))
- return false;
- }
-
- if ($p_type == "5") {
- $v_size = sprintf("%011s", DecOct(0));
- } else {
- $v_size = sprintf("%011s", DecOct($p_size));
- }
-
- $v_uid = sprintf("%07s", DecOct($p_uid));
- $v_gid = sprintf("%07s", DecOct($p_gid));
- $v_perms = sprintf("%07s", DecOct($p_perms & 000777));
-
- $v_mtime = sprintf("%11s", DecOct($p_mtime));
-
- $v_linkname = '';
-
- $v_magic = 'ustar ';
-
- $v_version = ' ';
-
- if (function_exists('posix_getpwuid'))
- {
- $userinfo = posix_getpwuid($p_uid);
- $groupinfo = posix_getgrgid($p_gid);
-
- $v_uname = $userinfo['name'];
- $v_gname = $groupinfo['name'];
- }
- else
- {
- $v_uname = '';
- $v_gname = '';
- }
-
- $v_devmajor = '';
-
- $v_devminor = '';
-
- $v_prefix = '';
-
- $v_binary_data_first = pack("a100a8a8a8a12A12",
- $p_filename, $v_perms, $v_uid, $v_gid,
- $v_size, $v_mtime);
- $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
- $p_type, $v_linkname, $v_magic,
- $v_version, $v_uname, $v_gname,
- $v_devmajor, $v_devminor, $v_prefix, '');
-
- // ----- Calculate the checksum
- $v_checksum = 0;
- // ..... First part of the header
- for ($i=0; $i<148; $i++)
- $v_checksum += ord(substr($v_binary_data_first,$i,1));
- // ..... Ignore the checksum value and replace it by ' ' (space)
- for ($i=148; $i<156; $i++)
- $v_checksum += ord(' ');
- // ..... Last part of the header
- for ($i=156, $j=0; $i<512; $i++, $j++)
- $v_checksum += ord(substr($v_binary_data_last,$j,1));
-
- // ----- Write the first 148 bytes of the header in the archive
- $this->_writeBlock($v_binary_data_first, 148);
-
- // ----- Write the calculated checksum
- $v_checksum = sprintf("%06s ", DecOct($v_checksum));
- $v_binary_data = pack("a8", $v_checksum);
- $this->_writeBlock($v_binary_data, 8);
-
- // ----- Write the last 356 bytes of the header in the archive
- $this->_writeBlock($v_binary_data_last, 356);
-
- return true;
- }
- // }}}
-
- // {{{ _writeLongHeader()
- function _writeLongHeader($p_filename)
- {
- $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
-
- $v_typeflag = 'L';
-
- $v_linkname = '';
-
- $v_magic = '';
-
- $v_version = '';
-
- $v_uname = '';
-
- $v_gname = '';
-
- $v_devmajor = '';
-
- $v_devminor = '';
-
- $v_prefix = '';
-
- $v_binary_data_first = pack("a100a8a8a8a12a12",
- '././@LongLink', 0, 0, 0, $v_size, 0);
- $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
- $v_typeflag, $v_linkname, $v_magic,
- $v_version, $v_uname, $v_gname,
- $v_devmajor, $v_devminor, $v_prefix, '');
-
- // ----- Calculate the checksum
- $v_checksum = 0;
- // ..... First part of the header
- for ($i=0; $i<148; $i++)
- $v_checksum += ord(substr($v_binary_data_first,$i,1));
- // ..... Ignore the checksum value and replace it by ' ' (space)
- for ($i=148; $i<156; $i++)
- $v_checksum += ord(' ');
- // ..... Last part of the header
- for ($i=156, $j=0; $i<512; $i++, $j++)
- $v_checksum += ord(substr($v_binary_data_last,$j,1));
-
- // ----- Write the first 148 bytes of the header in the archive
- $this->_writeBlock($v_binary_data_first, 148);
-
- // ----- Write the calculated checksum
- $v_checksum = sprintf("%06s ", DecOct($v_checksum));
- $v_binary_data = pack("a8", $v_checksum);
- $this->_writeBlock($v_binary_data, 8);
-
- // ----- Write the last 356 bytes of the header in the archive
- $this->_writeBlock($v_binary_data_last, 356);
-
- // ----- Write the filename as content of the block
- $i=0;
- while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
- $v_binary_data = pack("a512", "$v_buffer");
- $this->_writeBlock($v_binary_data);
- }
-
- return true;
- }
- // }}}
-
- // {{{ _readHeader()
- function _readHeader($v_binary_data, &$v_header)
- {
- if (strlen($v_binary_data)==0) {
- $v_header['filename'] = '';
- return true;
- }
-
- if (strlen($v_binary_data) != 512) {
- $v_header['filename'] = '';
- $this->_error('Invalid block size : '.strlen($v_binary_data));
- return false;
- }
-
- if (!is_array($v_header)) {
- $v_header = array();
- }
- // ----- Calculate the checksum
- $v_checksum = 0;
- // ..... First part of the header
- for ($i=0; $i<148; $i++)
- $v_checksum+=ord(substr($v_binary_data,$i,1));
- // ..... Ignore the checksum value and replace it by ' ' (space)
- for ($i=148; $i<156; $i++)
- $v_checksum += ord(' ');
- // ..... Last part of the header
- for ($i=156; $i<512; $i++)
- $v_checksum+=ord(substr($v_binary_data,$i,1));
-
- if (version_compare(PHP_VERSION,"5.5.0-dev")<0) {
- $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
- "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
- "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
- } else {
- $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
- "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
- "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
- }
- $v_data = unpack($fmt, $v_binary_data);
-
- if (strlen($v_data["prefix"]) > 0) {
- $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
- }
-
- // ----- Extract the checksum
- $v_header['checksum'] = OctDec(trim($v_data['checksum']));
- if ($v_header['checksum'] != $v_checksum) {
- $v_header['filename'] = '';
-
- // ----- Look for last block (empty block)
- if (($v_checksum == 256) && ($v_header['checksum'] == 0))
- return true;
-
- $this->_error('Invalid checksum for file "'.$v_data['filename']
- .'" : '.$v_checksum.' calculated, '
- .$v_header['checksum'].' expected');
- return false;
- }
-
- // ----- Extract the properties
- $v_header['filename'] = $v_data['filename'];
- if ($this->_maliciousFilename($v_header['filename'])) {
- $this->_error('Malicious .tar detected, file "' . $v_header['filename'] .
- '" will not install in desired directory tree');
- return false;
- }
- $v_header['mode'] = OctDec(trim($v_data['mode']));
- $v_header['uid'] = OctDec(trim($v_data['uid']));
- $v_header['gid'] = OctDec(trim($v_data['gid']));
- $v_header['size'] = OctDec(trim($v_data['size']));
- $v_header['mtime'] = OctDec(trim($v_data['mtime']));
- if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
- $v_header['size'] = 0;
- }
- $v_header['link'] = trim($v_data['link']);
- /* ----- All these fields are removed form the header because
- they do not carry interesting info
- $v_header[magic] = trim($v_data[magic]);
- $v_header[version] = trim($v_data[version]);
- $v_header[uname] = trim($v_data[uname]);
- $v_header[gname] = trim($v_data[gname]);
- $v_header[devmajor] = trim($v_data[devmajor]);
- $v_header[devminor] = trim($v_data[devminor]);
- */
-
- return true;
- }
- // }}}
-
- // {{{ _maliciousFilename()
- /**
- * Detect and report a malicious file name
- *
- * @param string $file
- *
- * @return bool
- * @access private
- */
- function _maliciousFilename($file)
- {
- if (strpos($file, '/../') !== false) {
- return true;
- }
- if (strpos($file, '../') === 0) {
- return true;
- }
- return false;
- }
- // }}}
-
- // {{{ _readLongHeader()
- function _readLongHeader(&$v_header)
- {
- $v_filename = '';
- $n = floor($v_header['size']/512);
- for ($i=0; $i<$n; $i++) {
- $v_content = $this->_readBlock();
- $v_filename .= $v_content;
- }
- if (($v_header['size'] % 512) != 0) {
- $v_content = $this->_readBlock();
- $v_filename .= trim($v_content);
- }
-
- // ----- Read the next header
- $v_binary_data = $this->_readBlock();
-
- if (!$this->_readHeader($v_binary_data, $v_header))
- return false;
-
- $v_filename = trim($v_filename);
- $v_header['filename'] = $v_filename;
- if ($this->_maliciousFilename($v_filename)) {
- $this->_error('Malicious .tar detected, file "' . $v_filename .
- '" will not install in desired directory tree');
- return false;
- }
-
- return true;
- }
- // }}}
-
- // {{{ _extractInString()
- /**
- * This method extract from the archive one file identified by $p_filename.
- * The return value is a string with the file content, or null on error.
- *
- * @param string $p_filename The path of the file to extract in a string.
- *
- * @return a string with the file content or null.
- * @access private
- */
- function _extractInString($p_filename)
- {
- $v_result_str = "";
-
- While (strlen($v_binary_data = $this->_readBlock()) != 0)
- {
- if (!$this->_readHeader($v_binary_data, $v_header))
- return null;
-
- if ($v_header['filename'] == '')
- continue;
-
- // ----- Look for long filename
- if ($v_header['typeflag'] == 'L') {
- if (!$this->_readLongHeader($v_header))
- return null;
- }
-
- if ($v_header['filename'] == $p_filename) {
- if ($v_header['typeflag'] == "5") {
- $this->_error('Unable to extract in string a directory '
- .'entry {'.$v_header['filename'].'}');
- return null;
- } else {
- $n = floor($v_header['size']/512);
- for ($i=0; $i<$n; $i++) {
- $v_result_str .= $this->_readBlock();
- }
- if (($v_header['size'] % 512) != 0) {
- $v_content = $this->_readBlock();
- $v_result_str .= substr($v_content, 0,
- ($v_header['size'] % 512));
- }
- return $v_result_str;
- }
- } else {
- $this->_jumpBlock(ceil(($v_header['size']/512)));
- }
- }
-
- return null;
- }
- // }}}
-
- // {{{ _extractList()
- function _extractList($p_path, &$p_list_detail, $p_mode,
- $p_file_list, $p_remove_path, $p_preserve=false)
- {
- $v_result=true;
- $v_nb = 0;
- $v_extract_all = true;
- $v_listing = false;
-
- $p_path = $this->_translateWinPath($p_path, false);
- if ($p_path == '' || (substr($p_path, 0, 1) != '/'
- && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
- $p_path = "./".$p_path;
- }
- $p_remove_path = $this->_translateWinPath($p_remove_path);
-
- // ----- Look for path to remove format (should end by /)
- if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
- $p_remove_path .= '/';
- $p_remove_path_size = strlen($p_remove_path);
-
- switch ($p_mode) {
- case "complete" :
- $v_extract_all = true;
- $v_listing = false;
- break;
- case "partial" :
- $v_extract_all = false;
- $v_listing = false;
- break;
- case "list" :
- $v_extract_all = false;
- $v_listing = true;
- break;
- default :
- $this->_error('Invalid extract mode ('.$p_mode.')');
- return false;
- }
-
- clearstatcache();
-
- while (strlen($v_binary_data = $this->_readBlock()) != 0)
- {
- $v_extract_file = FALSE;
- $v_extraction_stopped = 0;
-
- if (!$this->_readHeader($v_binary_data, $v_header))
- return false;
-
- if ($v_header['filename'] == '') {
- continue;
- }
-
- // ----- Look for long filename
- if ($v_header['typeflag'] == 'L') {
- if (!$this->_readLongHeader($v_header))
- return false;
- }
-
- if ((!$v_extract_all) && (is_array($p_file_list))) {
- // ----- By default no unzip if the file is not found
- $v_extract_file = false;
-
- for ($i=0; $i<sizeof($p_file_list); $i++) {
- // ----- Look if it is a directory
- if (substr($p_file_list[$i], -1) == '/') {
- // ----- Look if the directory is in the filename path
- if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
- && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
- == $p_file_list[$i])) {
- $v_extract_file = true;
- break;
- }
- }
-
- // ----- It is a file, so compare the file names
- elseif ($p_file_list[$i] == $v_header['filename']) {
- $v_extract_file = true;
- break;
- }
- }
- } else {
- $v_extract_file = true;
- }
-
- // ----- Look if this file need to be extracted
- if (($v_extract_file) && (!$v_listing))
- {
- if (($p_remove_path != '')
- && (substr($v_header['filename'].'/', 0, $p_remove_path_size)
- == $p_remove_path)) {
- $v_header['filename'] = substr($v_header['filename'],
- $p_remove_path_size);
- if( $v_header['filename'] == '' ){
- continue;
- }
- }
- if (($p_path != './') && ($p_path != '/')) {
- while (substr($p_path, -1) == '/')
- $p_path = substr($p_path, 0, strlen($p_path)-1);
-
- if (substr($v_header['filename'], 0, 1) == '/')
- $v_header['filename'] = $p_path.$v_header['filename'];
- else
- $v_header['filename'] = $p_path.'/'.$v_header['filename'];
- }
- if (file_exists($v_header['filename'])) {
- if ( (@is_dir($v_header['filename']))
- && ($v_header['typeflag'] == '')) {
- $this->_error('File '.$v_header['filename']
- .' already exists as a directory');
- return false;
- }
- if ( ($this->_isArchive($v_header['filename']))
- && ($v_header['typeflag'] == "5")) {
- $this->_error('Directory '.$v_header['filename']
- .' already exists as a file');
- return false;
- }
- if (!is_writeable($v_header['filename'])) {
- $this->_error('File '.$v_header['filename']
- .' already exists and is write protected');
- return false;
- }
- if (filemtime($v_header['filename']) > $v_header['mtime']) {
- // To be completed : An error or silent no replace ?
- }
- }
-
- // ----- Check the directory availability and create it if necessary
- elseif (($v_result
- = $this->_dirCheck(($v_header['typeflag'] == "5"
- ?$v_header['filename']
- :dirname($v_header['filename'])))) != 1) {
- $this->_error('Unable to create path for '.$v_header['filename']);
- return false;
- }
-
- if ($v_extract_file) {
- if ($v_header['typeflag'] == "5") {
- if (!@file_exists($v_header['filename'])) {
- if (!@mkdir($v_header['filename'], 0777)) {
- $this->_error('Unable to create directory {'
- .$v_header['filename'].'}');
- return false;
- }
- }
- } elseif ($v_header['typeflag'] == "2") {
- if (@file_exists($v_header['filename'])) {
- @unlink($v_header['filename']);
- }
- if (!@symlink($v_header['link'], $v_header['filename'])) {
- $this->_error('Unable to extract symbolic link {'
- .$v_header['filename'].'}');
- return false;
- }
- } else {
- if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
- $this->_error('Error while opening {'.$v_header['filename']
- .'} in write binary mode');
- return false;
- } else {
- $n = floor($v_header['size']/512);
- for ($i=0; $i<$n; $i++) {
- $v_content = $this->_readBlock();
- fwrite($v_dest_file, $v_content, 512);
- }
- if (($v_header['size'] % 512) != 0) {
- $v_content = $this->_readBlock();
- fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
- }
-
- @fclose($v_dest_file);
-
- if ($p_preserve) {
- @chown($v_header['filename'], $v_header['uid']);
- @chgrp($v_header['filename'], $v_header['gid']);
- }
-
- // ----- Change the file mode, mtime
- @touch($v_header['filename'], $v_header['mtime']);
- if ($v_header['mode'] & 0111) {
- // make file executable, obey umask
- $mode = fileperms($v_header['filename']) | (~umask() & 0111);
- @chmod($v_header['filename'], $mode);
- }
- }
-
- // ----- Check the file size
- clearstatcache();
- if (!is_file($v_header['filename'])) {
- $this->_error('Extracted file '.$v_header['filename']
- .'does not exist. Archive may be corrupted.');
- return false;
- }
-
- $filesize = filesize($v_header['filename']);
- if ($filesize != $v_header['size']) {
- $this->_error('Extracted file '.$v_header['filename']
- .' does not have the correct file size \''
- .$filesize
- .'\' ('.$v_header['size']
- .' expected). Archive may be corrupted.');
- return false;
- }
- }
- } else {
- $this->_jumpBlock(ceil(($v_header['size']/512)));
- }
- } else {
- $this->_jumpBlock(ceil(($v_header['size']/512)));
- }
-
- /* TBC : Seems to be unused ...
- if ($this->_compress)
- $v_end_of_file = @gzeof($this->_file);
- else
- $v_end_of_file = @feof($this->_file);
- */
-
- if ($v_listing || $v_extract_file || $v_extraction_stopped) {
- // ----- Log extracted files
- if (($v_file_dir = dirname($v_header['filename']))
- == $v_header['filename'])
- $v_file_dir = '';
- if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
- $v_file_dir = '/';
-
- $p_list_detail[$v_nb++] = $v_header;
- if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
- return true;
- }
- }
- }
-
- return true;
- }
- // }}}
-
- // {{{ _openAppend()
- function _openAppend()
- {
- if (filesize($this->_tarname) == 0)
- return $this->_openWrite();
-
- if ($this->_compress) {
- $this->_close();
-
- if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
- $this->_error('Error while renaming \''.$this->_tarname
- .'\' to temporary file \''.$this->_tarname
- .'.tmp\'');
- return false;
- }
-
- if ($this->_compress_type == 'gz')
- $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
- elseif ($this->_compress_type == 'bz2')
- $v_temp_tar = @bzopen($this->_tarname.".tmp", "r");
-
- if ($v_temp_tar == 0) {
- $this->_error('Unable to open file \''.$this->_tarname
- .'.tmp\' in binary read mode');
- @rename($this->_tarname.".tmp", $this->_tarname);
- return false;
- }
-
- if (!$this->_openWrite()) {
- @rename($this->_tarname.".tmp", $this->_tarname);
- return false;
- }
-
- if ($this->_compress_type == 'gz') {
- $end_blocks = 0;
-
- while (!@gzeof($v_temp_tar)) {
- $v_buffer = @gzread($v_temp_tar, 512);
- if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
- $end_blocks++;
- // do not copy end blocks, we will re-make them
- // after appending
- continue;
- } elseif ($end_blocks > 0) {
- for ($i = 0; $i < $end_blocks; $i++) {
- $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
- }
- $end_blocks = 0;
- }
- $v_binary_data = pack("a512", $v_buffer);
- $this->_writeBlock($v_binary_data);
- }
-
- @gzclose($v_temp_tar);
- }
- elseif ($this->_compress_type == 'bz2') {
- $end_blocks = 0;
-
- while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
- if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
- $end_blocks++;
- // do not copy end blocks, we will re-make them
- // after appending
- continue;
- } elseif ($end_blocks > 0) {
- for ($i = 0; $i < $end_blocks; $i++) {
- $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
- }
- $end_blocks = 0;
- }
- $v_binary_data = pack("a512", $v_buffer);
- $this->_writeBlock($v_binary_data);
- }
-
- @bzclose($v_temp_tar);
- }
-
- if (!@unlink($this->_tarname.".tmp")) {
- $this->_error('Error while deleting temporary file \''
- .$this->_tarname.'.tmp\'');
- }
-
- } else {
- // ----- For not compressed tar, just add files before the last
- // one or two 512 bytes block
- if (!$this->_openReadWrite())
- return false;
-
- clearstatcache();
- $v_size = filesize($this->_tarname);
-
- // We might have zero, one or two end blocks.
- // The standard is two, but we should try to handle
- // other cases.
- fseek($this->_file, $v_size - 1024);
- if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
- fseek($this->_file, $v_size - 1024);
- }
- elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
- fseek($this->_file, $v_size - 512);
- }
- }
-
- return true;
- }
- // }}}
-
- // {{{ _append()
- function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
- {
- if (!$this->_openAppend())
- return false;
-
- if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
- $this->_writeFooter();
-
- $this->_close();
-
- return true;
- }
- // }}}
-
- // {{{ _dirCheck()
-
- /**
- * Check if a directory exists and create it (including parent
- * dirs) if not.
- *
- * @param string $p_dir directory to check
- *
- * @return bool true if the directory exists or was created
- */
- function _dirCheck($p_dir)
- {
- clearstatcache();
- if ((@is_dir($p_dir)) || ($p_dir == ''))
- return true;
-
- $p_parent_dir = dirname($p_dir);
-
- if (($p_parent_dir != $p_dir) &&
- ($p_parent_dir != '') &&
- (!$this->_dirCheck($p_parent_dir)))
- return false;
-
- if (!@mkdir($p_dir, 0777)) {
- $this->_error("Unable to create directory '$p_dir'");
- return false;
- }
-
- return true;
- }
-
- // }}}
-
- // {{{ _pathReduction()
-
- /**
- * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
- * rand emove double slashes.
- *
- * @param string $p_dir path to reduce
- *
- * @return string reduced path
- *
- * @access private
- *
- */
- function _pathReduction($p_dir)
- {
- $v_result = '';
-
- // ----- Look for not empty path
- if ($p_dir != '') {
- // ----- Explode path by directory names
- $v_list = explode('/', $p_dir);
-
- // ----- Study directories from last to first
- for ($i=sizeof($v_list)-1; $i>=0; $i--) {
- // ----- Look for current path
- if ($v_list[$i] == ".") {
- // ----- Ignore this directory
- // Should be the first $i=0, but no check is done
- }
- else if ($v_list[$i] == "..") {
- // ----- Ignore it and ignore the $i-1
- $i--;
- }
- else if ( ($v_list[$i] == '')
- && ($i!=(sizeof($v_list)-1))
- && ($i!=0)) {
- // ----- Ignore only the double '//' in path,
- // but not the first and last /
- } else {
- $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
- .$v_result:'');
- }
- }
- }
-
- if (defined('OS_WINDOWS') && OS_WINDOWS) {
- $v_result = strtr($v_result, '\\', '/');
- }
-
- return $v_result;
- }
-
- // }}}
-
- // {{{ _translateWinPath()
- function _translateWinPath($p_path, $p_remove_disk_letter=true)
- {
- if (defined('OS_WINDOWS') && OS_WINDOWS) {
- // ----- Look for potential disk letter
- if ( ($p_remove_disk_letter)
- && (($v_position = strpos($p_path, ':')) != false)) {
- $p_path = substr($p_path, $v_position+1);
- }
- // ----- Change potential windows directory separator
- if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
- $p_path = strtr($p_path, '\\', '/');
- }
- }
- return $p_path;
- }
- // }}}
-
-}
-?>
diff --git a/libs/PiwikTracker/PiwikTracker.php b/libs/PiwikTracker/PiwikTracker.php
index 8605e38e1f..15e3b8420c 100644
--- a/libs/PiwikTracker/PiwikTracker.php
+++ b/libs/PiwikTracker/PiwikTracker.php
@@ -1063,6 +1063,8 @@ class PiwikTracker
/**
* Hash function used internally by Piwik to hash a User ID into the Visitor ID.
*
+ * Note: matches implementation of Tracker\Request->getUserIdHashed()
+ *
* @param $id
* @return string
*/
@@ -1071,7 +1073,6 @@ class PiwikTracker
return substr( sha1( $id ), 0, 16);
}
-
/**
* Forces the requests to be recorded for the specified Visitor ID.
* Note: it is recommended to use ->setUserId($userId); instead.
diff --git a/libs/README.md b/libs/README.md
index 959d38e4b9..8d7c7d8e6e 100644
--- a/libs/README.md
+++ b/libs/README.md
@@ -17,8 +17,6 @@ third-party libraries:
- The bug #4206 (GD with JIS-mapped Japanese Font Support) was fixed in this
commit: https://github.com/piwik/piwik/commit/516c13d9b13ca3b908575eb809f7ad9d9397f0e1
Changed files: class/pImage.class.php class/pDraw.class.php
- * PclZip/
- - in r1960, ignore touch() - utime failed warning
* PEAR/, PEAR.php
- in r2419, add static keyword to isError and raiseError as it throws notices
in HTML_Quickform2
diff --git a/misc/phpstorm-codestyles/Piwik_codestyle.xml b/misc/phpstorm-codestyles/Piwik_codestyle.xml
index 0fd9a921b5..ed09f367d7 100644
--- a/misc/phpstorm-codestyles/Piwik_codestyle.xml
+++ b/misc/phpstorm-codestyles/Piwik_codestyle.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="UTF-8"?>
<code_scheme name="Piwik-codestyle">
<option name="LINE_SEPARATOR" value="&#10;" />
<option name="RIGHT_MARGIN" value="160" />
@@ -24,136 +23,6 @@
<order>BREADTH_FIRST</order>
</group>
</groups>
- <rules>
- <rule>
- <match>
- <CONST />
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <FIELD />
- <PUBLIC />
- <STATIC />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <FIELD />
- <PROTECTED />
- <STATIC />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <FIELD />
- <PRIVATE />
- <STATIC />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <FIELD />
- <PUBLIC />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <FIELD />
- <PROTECTED />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <FIELD />
- <PRIVATE />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <CONSTRUCTOR />
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <METHOD />
- <PUBLIC />
- <STATIC />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <METHOD />
- <PROTECTED />
- <STATIC />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <METHOD />
- <PRIVATE />
- <STATIC />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <METHOD />
- <PUBLIC />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <METHOD />
- <PROTECTED />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <AND>
- <METHOD />
- <PRIVATE />
- </AND>
- </match>
- </rule>
- <rule>
- <match>
- <TRAIT />
- </match>
- </rule>
- <rule>
- <match>
- <INTERFACE />
- </match>
- </rule>
- <rule>
- <match>
- <CLASS />
- </match>
- </rule>
- </rules>
</arrangement>
</codeStyleSettings>
-</code_scheme>
-
+</code_scheme> \ No newline at end of file
diff --git a/misc/proxy-hide-piwik-url/piwik.php b/misc/proxy-hide-piwik-url/piwik.php
index 80721cd6ea..d1c9e9ca3c 100644
--- a/misc/proxy-hide-piwik-url/piwik.php
+++ b/misc/proxy-hide-piwik-url/piwik.php
@@ -76,7 +76,7 @@ if (empty($_GET)) {
$url = sprintf("%spiwik.php?cip=%s&token_auth=%s&", $PIWIK_URL, getVisitIp(), $TOKEN_AUTH);
foreach ($_GET as $key => $value) {
- $url .= $key . '=' . urlencode($value) . '&';
+ $url .= urlencode($key ). '=' . urlencode($value) . '&';
}
sendHeader("Content-Type: image/gif");
$stream_options = array('http' => array(
diff --git a/plugins/API/API.php b/plugins/API/API.php
index 00758ca6bb..c65c04fffe 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -259,7 +259,7 @@ class API extends \Piwik\Plugin\API
SegmentExpression::MATCH_IS_NULL_OR_EMPTY,
SegmentExpression::MATCH_NOT_EQUAL,
);
- if(in_array($matchType, $acceptedMatches)) {
+ if (in_array($matchType, $acceptedMatches)) {
return $value;
}
$message = "Invalid Segment match type: try using 'userId' segment with one of the following match types: %s.";
@@ -524,7 +524,7 @@ class API extends \Piwik\Plugin\API
*/
public function getSuggestedValuesForSegment($segmentName, $idSite)
{
- if(empty(Config::getInstance()->General['enable_segment_suggested_values'])) {
+ if (empty(Config::getInstance()->General['enable_segment_suggested_values'])) {
return array();
}
Piwik::checkUserHasViewAccess($idSite);
@@ -549,7 +549,7 @@ class API extends \Piwik\Plugin\API
}
// if period=range is disabled, do not proceed
- if(!Period\Factory::isPeriodEnabledForAPI('range')) {
+ if (!Period\Factory::isPeriodEnabledForAPI('range')) {
return array();
}
diff --git a/plugins/API/Controller.php b/plugins/API/Controller.php
index 9a6b3fc5e7..18e39d58a2 100644
--- a/plugins/API/Controller.php
+++ b/plugins/API/Controller.php
@@ -60,7 +60,7 @@ class Controller extends \Piwik\Plugin\Controller
foreach ($segments as $segment) {
// Eg. Event Value is a metric, not in the Visit metric category,
// we make sure it is displayed along with the Events dimensions
- if($segment['type'] == 'metric' && $segment['category'] != Piwik::translate('General_Visit')) {
+ if ($segment['type'] == 'metric' && $segment['category'] != Piwik::translate('General_Visit')) {
$segment['type'] = 'dimension';
}
diff --git a/plugins/API/RowEvolution.php b/plugins/API/RowEvolution.php
index 7a5290c44f..b57a788050 100644
--- a/plugins/API/RowEvolution.php
+++ b/plugins/API/RowEvolution.php
@@ -279,7 +279,7 @@ class RowEvolution
// note: some reports should not be filtered with AddColumnProcessedMetrics
// specifically, reports without the 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( isset($metadata['metrics']['nb_visits'])
+ if ( isset($metadata['metrics']['nb_visits'])
&& !empty($label)) {
$parameters['filter_add_columns_when_show_all_columns'] = '1';
}
@@ -430,7 +430,7 @@ class RowEvolution
$labelRow, $apiModule, $apiAction, $labelUseAbsoluteUrl);
$prettyLabel = $labelRow->getColumn('label_html');
- if($prettyLabel !== false) {
+ if ($prettyLabel !== false) {
$actualLabels[$labelIdx] = $prettyLabel;
}
diff --git a/plugins/Actions/Actions/ActionSiteSearch.php b/plugins/Actions/Actions/ActionSiteSearch.php
index 9631aa0473..d19cfaa6fc 100644
--- a/plugins/Actions/Actions/ActionSiteSearch.php
+++ b/plugins/Actions/Actions/ActionSiteSearch.php
@@ -73,7 +73,7 @@ class ActionSiteSearch extends Action
{
$siteSearch = $this->detectSiteSearch($this->originalUrl);
- if(empty($siteSearch)) {
+ if (empty($siteSearch)) {
return false;
}
@@ -169,10 +169,10 @@ class ActionSiteSearch extends Action
// @see excludeQueryParametersFromUrl()
// Excluded the detected parameters from the URL
$parametersToExclude = array($categoryParameterRaw, $keywordParameterRaw);
- if(isset($parsedUrl['query'])) {
+ if (isset($parsedUrl['query'])) {
$parsedUrl['query'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['query']), $parametersToExclude);
}
- if(isset($parsedUrl['fragment'])) {
+ if (isset($parsedUrl['fragment'])) {
$parsedUrl['fragment'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['fragment']), $parametersToExclude);
}
}
diff --git a/plugins/Actions/Archiver.php b/plugins/Actions/Archiver.php
index 14d4214744..67408ced64 100644
--- a/plugins/Actions/Archiver.php
+++ b/plugins/Actions/Archiver.php
@@ -241,7 +241,7 @@ class Archiver extends \Piwik\Plugin\Archiver
// 1) No result Keywords
// 2) For each page view, count number of times the referrer page was a Site Search
if ($this->isSiteSearchEnabled()) {
- $this->updateQuerySelectFromForSiteSearch($select, $from);
+ $this->updateQuerySelectFromForSiteSearch($select, $from);
}
$this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_name", $rankingQuery);
diff --git a/plugins/Annotations/AnnotationList.php b/plugins/Annotations/AnnotationList.php
index 89a5b9131f..cf068e6f35 100755
--- a/plugins/Annotations/AnnotationList.php
+++ b/plugins/Annotations/AnnotationList.php
@@ -331,7 +331,7 @@ class AnnotationList
if ($serialized !== false) {
$result[$id] = @unserialize($serialized);
- if(empty($result[$id])) {
+ if (empty($result[$id])) {
// in case unserialize failed
$result[$id] = array();
}
diff --git a/plugins/Contents/Controller.php b/plugins/Contents/Controller.php
index daf319bb53..5785ba478c 100644
--- a/plugins/Contents/Controller.php
+++ b/plugins/Contents/Controller.php
@@ -27,7 +27,7 @@ class Controller extends \Piwik\Plugin\Controller
$reportsView->addReport(
$report->getCategory(),
$report->getName(),
- 'Contents.menu' . ucfirst($report->getAction())
+ 'Contents.' . Report::PREFIX_ACTION_IN_MENU . ucfirst($report->getAction())
);
}
diff --git a/plugins/Contents/tests/ContentsTest.php b/plugins/Contents/tests/ContentsTest.php
index 2bd9549502..68875db127 100644
--- a/plugins/Contents/tests/ContentsTest.php
+++ b/plugins/Contents/tests/ContentsTest.php
@@ -36,7 +36,7 @@ class ContentsTest extends IntegrationTestCase
'Contents.getContentPieces',
'Actions.get',
'Actions.getPageUrls',
- //'Live.getLastVisitsDetails'
+ 'Live.getLastVisitsDetails'
);
}
@@ -55,7 +55,7 @@ class ContentsTest extends IntegrationTestCase
$dayPeriod = 'day';
$periods = array($dayPeriod, 'month');
- $apisToTest = array('Contents', 'Actions.getPageUrls'); // , 'Live.getLastVisitsDetails'
+ $apisToTest = array('Contents', 'Actions.getPageUrls', 'Live.getLastVisitsDetails');
$result = array(
array($apiToCallProcessedReportMetadata, array(
'idSite' => $idSite1,
diff --git a/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_day.xml b/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_day.xml
index c1e492c05a..fde347f2b5 100644
--- a/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_day.xml
+++ b/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_day.xml
@@ -5,18 +5,18 @@
<nb_visits>2</nb_visits>
<nb_uniq_visitors>2</nb_uniq_visitors>
<nb_hits>2</nb_hits>
- <sum_time_spent>0</sum_time_spent>
+ <sum_time_spent>540</sum_time_spent>
<nb_hits_with_time_generation>2</nb_hits_with_time_generation>
<min_time_generation>0.333</min_time_generation>
<max_time_generation>0.333</max_time_generation>
<entry_nb_uniq_visitors>2</entry_nb_uniq_visitors>
<entry_nb_visits>2</entry_nb_visits>
<entry_nb_actions>2</entry_nb_actions>
- <entry_sum_visit_length>2</entry_sum_visit_length>
+ <entry_sum_visit_length>542</entry_sum_visit_length>
<entry_bounce_count>2</entry_bounce_count>
<exit_nb_uniq_visitors>2</exit_nb_uniq_visitors>
<exit_nb_visits>2</exit_nb_visits>
- <avg_time_on_page>0</avg_time_on_page>
+ <avg_time_on_page>270</avg_time_on_page>
<bounce_rate>100%</bounce_rate>
<exit_rate>100%</exit_rate>
<avg_time_generation>0.333</avg_time_generation>
diff --git a/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_month.xml b/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_month.xml
index 56760f4816..3dc775d40a 100644
--- a/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_month.xml
+++ b/plugins/Contents/tests/expected/test_Contents__Actions.getPageUrls_month.xml
@@ -4,19 +4,19 @@
<label>/page</label>
<nb_visits>2</nb_visits>
<nb_hits>2</nb_hits>
- <sum_time_spent>0</sum_time_spent>
+ <sum_time_spent>540</sum_time_spent>
<nb_hits_with_time_generation>2</nb_hits_with_time_generation>
<min_time_generation>0.333</min_time_generation>
<max_time_generation>0.333</max_time_generation>
<entry_nb_visits>2</entry_nb_visits>
<entry_nb_actions>2</entry_nb_actions>
- <entry_sum_visit_length>2</entry_sum_visit_length>
+ <entry_sum_visit_length>542</entry_sum_visit_length>
<entry_bounce_count>2</entry_bounce_count>
<exit_nb_visits>2</exit_nb_visits>
<sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors>
<sum_daily_entry_nb_uniq_visitors>2</sum_daily_entry_nb_uniq_visitors>
<sum_daily_exit_nb_uniq_visitors>2</sum_daily_exit_nb_uniq_visitors>
- <avg_time_on_page>0</avg_time_on_page>
+ <avg_time_on_page>270</avg_time_on_page>
<bounce_rate>100%</bounce_rate>
<exit_rate>100%</exit_rate>
<avg_time_generation>0.333</avg_time_generation>
diff --git a/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_day.xml b/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_day.xml
index 6a083ba448..989f816722 100644
--- a/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_day.xml
+++ b/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_day.xml
@@ -2,449 +2,8 @@
<result>
<row>
<idSite>1</idSite>
- <idVisit>6</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>91</visitDuration>
- <visitDurationPretty>1 min 31s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>4</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>61</visitDuration>
- <visitDurationPretty>1 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>5</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>1</visitDuration>
- <visitDurationPretty>1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>2</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>121</visitDuration>
- <visitDurationPretty>2 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>3</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
<idVisit>1</idVisit>
<visitIp>156.5.3.2</visitIp>
- <userId />
<actionDetails>
<row>
@@ -469,6 +28,7 @@
<searches>0</searches>
<actions>1</actions>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -479,8 +39,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>1</visitDuration>
- <visitDurationPretty>1s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
@@ -542,179 +102,22 @@
</row>
<row>
<idSite>1</idSite>
- <idVisit>13</idVisit>
- <visitIp>111.1.1.1</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>91</visitDuration>
- <visitDurationPretty>1 min 31s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>8</idVisit>
+ <idVisit>2</idVisit>
<visitIp>111.1.1.1</visitIp>
- <userId />
<actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>181</visitDuration>
- <visitDurationPretty>3 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
<row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
+ <type>action</type>
+ <url>http://www.example.org/page</url>
+ <pageTitle>Ads</pageTitle>
+ <pageIdAction>2</pageIdAction>
+
+ <pageId>13</pageId>
+ <generationTime>0.33s</generationTime>
+ <timeSpent>0</timeSpent>
+ <timeSpentPretty>0s</timeSpentPretty>
+ <icon />
</row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>11</idVisit>
- <visitIp>111.1.1.1</visitIp>
- <userId />
-
- <actionDetails>
</actionDetails>
<goalConversions>0</goalConversions>
<siteCurrency>USD</siteCurrency>
@@ -724,91 +127,8 @@
<searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>12</idVisit>
- <visitIp>111.1.1.1</visitIp>
+ <actions>1</actions>
<userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -819,8 +139,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
diff --git a/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_month.xml b/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_month.xml
index 6a083ba448..989f816722 100644
--- a/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_month.xml
+++ b/plugins/Contents/tests/expected/test_Contents__Live.getLastVisitsDetails_month.xml
@@ -2,449 +2,8 @@
<result>
<row>
<idSite>1</idSite>
- <idVisit>6</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>91</visitDuration>
- <visitDurationPretty>1 min 31s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>4</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>61</visitDuration>
- <visitDurationPretty>1 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>5</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>1</visitDuration>
- <visitDurationPretty>1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>2</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>121</visitDuration>
- <visitDurationPretty>2 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>3</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
<idVisit>1</idVisit>
<visitIp>156.5.3.2</visitIp>
- <userId />
<actionDetails>
<row>
@@ -469,6 +28,7 @@
<searches>0</searches>
<actions>1</actions>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -479,8 +39,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>1</visitDuration>
- <visitDurationPretty>1s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
@@ -542,179 +102,22 @@
</row>
<row>
<idSite>1</idSite>
- <idVisit>13</idVisit>
- <visitIp>111.1.1.1</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>91</visitDuration>
- <visitDurationPretty>1 min 31s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>8</idVisit>
+ <idVisit>2</idVisit>
<visitIp>111.1.1.1</visitIp>
- <userId />
<actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>181</visitDuration>
- <visitDurationPretty>3 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
<row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
+ <type>action</type>
+ <url>http://www.example.org/page</url>
+ <pageTitle>Ads</pageTitle>
+ <pageIdAction>2</pageIdAction>
+
+ <pageId>13</pageId>
+ <generationTime>0.33s</generationTime>
+ <timeSpent>0</timeSpent>
+ <timeSpentPretty>0s</timeSpentPretty>
+ <icon />
</row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>11</idVisit>
- <visitIp>111.1.1.1</visitIp>
- <userId />
-
- <actionDetails>
</actionDetails>
<goalConversions>0</goalConversions>
<siteCurrency>USD</siteCurrency>
@@ -724,91 +127,8 @@
<searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>12</idVisit>
- <visitIp>111.1.1.1</visitIp>
+ <actions>1</actions>
<userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -819,8 +139,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
diff --git a/plugins/Contents/tests/expected/test_Contents_contentInteractionMatch__Live.getLastVisitsDetails_day.xml b/plugins/Contents/tests/expected/test_Contents_contentInteractionMatch__Live.getLastVisitsDetails_day.xml
index dfd70cba41..989f816722 100644
--- a/plugins/Contents/tests/expected/test_Contents_contentInteractionMatch__Live.getLastVisitsDetails_day.xml
+++ b/plugins/Contents/tests/expected/test_Contents_contentInteractionMatch__Live.getLastVisitsDetails_day.xml
@@ -2,99 +2,22 @@
<result>
<row>
<idSite>1</idSite>
- <idVisit>6</idVisit>
+ <idVisit>1</idVisit>
<visitIp>156.5.3.2</visitIp>
- <userId />
<actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>91</visitDuration>
- <visitDurationPretty>1 min 31s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
<row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
+ <type>action</type>
+ <url>http://www.example.org/page</url>
+ <pageTitle>Ads</pageTitle>
+ <pageIdAction>2</pageIdAction>
+
+ <pageId>1</pageId>
+ <generationTime>0.33s</generationTime>
+ <timeSpent>0</timeSpent>
+ <timeSpentPretty>0s</timeSpentPretty>
+ <icon />
</row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>5</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
</actionDetails>
<goalConversions>0</goalConversions>
<siteCurrency>USD</siteCurrency>
@@ -104,7 +27,8 @@
<searches>0</searches>
- <actions>0</actions>
+ <actions>1</actions>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -115,8 +39,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>1</visitDuration>
- <visitDurationPretty>1s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
@@ -178,95 +102,22 @@
</row>
<row>
<idSite>1</idSite>
- <idVisit>13</idVisit>
+ <idVisit>2</idVisit>
<visitIp>111.1.1.1</visitIp>
- <userId />
<actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>91</visitDuration>
- <visitDurationPretty>1 min 31s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
<row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
+ <type>action</type>
+ <url>http://www.example.org/page</url>
+ <pageTitle>Ads</pageTitle>
+ <pageIdAction>2</pageIdAction>
+
+ <pageId>13</pageId>
+ <generationTime>0.33s</generationTime>
+ <timeSpent>0</timeSpent>
+ <timeSpentPretty>0s</timeSpentPretty>
+ <icon />
</row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>12</idVisit>
- <visitIp>111.1.1.1</visitIp>
- <userId />
-
- <actionDetails>
</actionDetails>
<goalConversions>0</goalConversions>
<siteCurrency>USD</siteCurrency>
@@ -276,7 +127,8 @@
<searches>0</searches>
- <actions>0</actions>
+ <actions>1</actions>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -287,8 +139,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
diff --git a/plugins/Contents/tests/expected/test_Contents_contentTargetMatch__Live.getLastVisitsDetails_day.xml b/plugins/Contents/tests/expected/test_Contents_contentTargetMatch__Live.getLastVisitsDetails_day.xml
index bed0f8ffd9..989f816722 100644
--- a/plugins/Contents/tests/expected/test_Contents_contentTargetMatch__Live.getLastVisitsDetails_day.xml
+++ b/plugins/Contents/tests/expected/test_Contents_contentTargetMatch__Live.getLastVisitsDetails_day.xml
@@ -2,99 +2,22 @@
<result>
<row>
<idSite>1</idSite>
- <idVisit>4</idVisit>
+ <idVisit>1</idVisit>
<visitIp>156.5.3.2</visitIp>
- <userId />
<actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>61</visitDuration>
- <visitDurationPretty>1 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
<row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
+ <type>action</type>
+ <url>http://www.example.org/page</url>
+ <pageTitle>Ads</pageTitle>
+ <pageIdAction>2</pageIdAction>
+
+ <pageId>1</pageId>
+ <generationTime>0.33s</generationTime>
+ <timeSpent>0</timeSpent>
+ <timeSpentPretty>0s</timeSpentPretty>
+ <icon />
</row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>2</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
</actionDetails>
<goalConversions>0</goalConversions>
<siteCurrency>USD</siteCurrency>
@@ -104,95 +27,8 @@
<searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>121</visitDuration>
- <visitDurationPretty>2 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>3</idVisit>
- <visitIp>156.5.3.2</visitIp>
+ <actions>1</actions>
<userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -203,8 +39,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
@@ -266,95 +102,22 @@
</row>
<row>
<idSite>1</idSite>
- <idVisit>8</idVisit>
+ <idVisit>2</idVisit>
<visitIp>111.1.1.1</visitIp>
- <userId />
<actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>181</visitDuration>
- <visitDurationPretty>3 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
<row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
+ <type>action</type>
+ <url>http://www.example.org/page</url>
+ <pageTitle>Ads</pageTitle>
+ <pageIdAction>2</pageIdAction>
+
+ <pageId>13</pageId>
+ <generationTime>0.33s</generationTime>
+ <timeSpent>0</timeSpent>
+ <timeSpentPretty>0s</timeSpentPretty>
+ <icon />
</row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>9</idVisit>
- <visitIp>111.1.1.1</visitIp>
- <userId />
-
- <actionDetails>
</actionDetails>
<goalConversions>0</goalConversions>
<siteCurrency>USD</siteCurrency>
@@ -364,91 +127,8 @@
<searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>10</idVisit>
- <visitIp>111.1.1.1</visitIp>
+ <actions>1</actions>
<userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -459,8 +139,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
diff --git a/plugins/Contents/tests/expected/test_ContentscontentNameOrPieceMatch__Live.getLastVisitsDetails_day.xml b/plugins/Contents/tests/expected/test_ContentscontentNameOrPieceMatch__Live.getLastVisitsDetails_day.xml
index 9595395b50..989f816722 100644
--- a/plugins/Contents/tests/expected/test_ContentscontentNameOrPieceMatch__Live.getLastVisitsDetails_day.xml
+++ b/plugins/Contents/tests/expected/test_ContentscontentNameOrPieceMatch__Live.getLastVisitsDetails_day.xml
@@ -2,449 +2,8 @@
<result>
<row>
<idSite>1</idSite>
- <idVisit>6</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>91</visitDuration>
- <visitDurationPretty>1 min 31s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>4</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>61</visitDuration>
- <visitDurationPretty>1 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>5</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>1</visitDuration>
- <visitDurationPretty>1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>2</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>121</visitDuration>
- <visitDurationPretty>2 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>3</idVisit>
- <visitIp>156.5.3.2</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>flash, java</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/flash.gif</pluginIcon>
- <pluginName>flash</pluginName>
- </row>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/java.gif</pluginIcon>
- <pluginName>java</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
<idVisit>1</idVisit>
<visitIp>156.5.3.2</visitIp>
- <userId />
<actionDetails>
<row>
@@ -469,6 +28,7 @@
<searches>0</searches>
<actions>1</actions>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -479,8 +39,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>1</visitDuration>
- <visitDurationPretty>1s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
@@ -542,179 +102,22 @@
</row>
<row>
<idSite>1</idSite>
- <idVisit>13</idVisit>
- <visitIp>111.1.1.1</visitIp>
- <userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>91</visitDuration>
- <visitDurationPretty>1 min 31s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>8</idVisit>
+ <idVisit>2</idVisit>
<visitIp>111.1.1.1</visitIp>
- <userId />
<actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>181</visitDuration>
- <visitDurationPretty>3 min 1s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
<row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
+ <type>action</type>
+ <url>http://www.example.org/page</url>
+ <pageTitle>Ads</pageTitle>
+ <pageIdAction>2</pageIdAction>
+
+ <pageId>13</pageId>
+ <generationTime>0.33s</generationTime>
+ <timeSpent>0</timeSpent>
+ <timeSpentPretty>0s</timeSpentPretty>
+ <icon />
</row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>12</idVisit>
- <visitIp>111.1.1.1</visitIp>
- <userId />
-
- <actionDetails>
</actionDetails>
<goalConversions>0</goalConversions>
<siteCurrency>USD</siteCurrency>
@@ -724,91 +127,8 @@
<searches>0</searches>
- <actions>0</actions>
- <visitorType>new</visitorType>
- <visitorTypeIcon />
- <visitConverted>0</visitConverted>
- <visitConvertedIcon />
- <visitCount>1</visitCount>
-
- <visitEcommerceStatus>none</visitEcommerceStatus>
- <visitEcommerceStatusIcon />
- <daysSinceFirstVisit>0</daysSinceFirstVisit>
- <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
- <customVariables>
- </customVariables>
- <deviceType>Desktop</deviceType>
- <events>0</events>
- <provider>Unknown</provider>
- <providerName>Unknown</providerName>
- <providerUrl>http://piwik.org/faq/general/#faq_52</providerUrl>
- <referrerType>direct</referrerType>
- <referrerTypeName>Direct Entry</referrerTypeName>
- <referrerName />
- <referrerKeyword />
- <referrerKeywordPosition />
- <referrerUrl />
- <referrerSearchEngineUrl />
- <referrerSearchEngineIcon />
- <continent>Europe</continent>
- <continentCode>eur</continentCode>
- <country>France</country>
- <countryCode>fr</countryCode>
- <countryFlag>plugins/UserCountry/images/flags/fr.png</countryFlag>
- <region />
- <regionCode />
- <city />
- <location>France</location>
- <latitude />
- <longitude />
- <operatingSystem>Windows XP</operatingSystem>
- <operatingSystemCode>WXP</operatingSystemCode>
- <operatingSystemShortName>Win XP</operatingSystemShortName>
- <operatingSystemIcon>plugins/UserSettings/images/os/WXP.gif</operatingSystemIcon>
- <browserFamily>gecko</browserFamily>
- <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
- <browserName>Firefox 3.6</browserName>
- <browserIcon>plugins/UserSettings/images/browsers/FF.gif</browserIcon>
- <browserCode>FF</browserCode>
- <browserVersion>3.6</browserVersion>
- <screenType>normal</screenType>
- <resolution>1024x768</resolution>
- <screenTypeIcon>plugins/UserSettings/images/screens/normal.gif</screenTypeIcon>
- <plugins>director</plugins>
- <pluginsIcons>
- <row>
- <pluginIcon>plugins/UserSettings/images/plugins/director.gif</pluginIcon>
- <pluginName>director</pluginName>
- </row>
- </pluginsIcons>
- <visitLocalTime>12:34:06</visitLocalTime>
- <visitLocalHour>12</visitLocalHour>
- <daysSinceLastVisit>0</daysSinceLastVisit>
-
-
-
-
-
- </row>
- <row>
- <idSite>1</idSite>
- <idVisit>9</idVisit>
- <visitIp>111.1.1.1</visitIp>
+ <actions>1</actions>
<userId />
-
- <actionDetails>
- </actionDetails>
- <goalConversions>0</goalConversions>
- <siteCurrency>USD</siteCurrency>
- <siteCurrencySymbol>$</siteCurrencySymbol>
-
-
-
-
- <searches>0</searches>
- <actions>0</actions>
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -819,8 +139,8 @@
<visitEcommerceStatusIcon />
<daysSinceFirstVisit>0</daysSinceFirstVisit>
<daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
- <visitDuration>0</visitDuration>
- <visitDurationPretty>0s</visitDurationPretty>
+ <visitDuration>271</visitDuration>
+ <visitDurationPretty>4 min 31s</visitDurationPretty>
<customVariables>
</customVariables>
<deviceType>Desktop</deviceType>
diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php
index 530cc10156..a25a522cb4 100644
--- a/plugins/CoreAdminHome/API.php
+++ b/plugins/CoreAdminHome/API.php
@@ -66,19 +66,23 @@ class API extends \Piwik\Plugin\API
public function invalidateArchivedReports($idSites, $dates, $period = false)
{
$idSites = Site::getIdSitesFromIdSitesString($idSites);
+
if (empty($idSites)) {
throw new Exception("Specify a value for &idSites= as a comma separated list of website IDs, for which your token_auth has 'admin' permission");
}
+
Piwik::checkUserHasAdminAccess($idSites);
- if(!empty($period)) {
+ if (!empty($period)) {
$period = Period\Factory::build($period, Date::today());
}
// Ensure the specified dates are valid
$toInvalidate = $invalidDates = array();
+
$dates = explode(',', trim($dates));
$dates = array_unique($dates);
+
foreach ($dates as $theDate) {
$theDate = trim($theDate);
try {
@@ -141,7 +145,7 @@ class API extends \Piwik\Plugin\API
}
}
- if(empty($minDate)) {
+ if (empty($minDate)) {
throw new Exception("Check the 'dates' parameter is a valid date.");
}
@@ -169,7 +173,7 @@ class API extends \Piwik\Plugin\API
$sql = implode(" OR ", $sql);
$sqlPeriod = "";
- if($invalidateForPeriod) {
+ if ($invalidateForPeriod) {
$sqlPeriod = " AND period = ? ";
$bind[] = $invalidateForPeriod;
}
diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php
index 6b672d07e8..d1278607fd 100644
--- a/plugins/CoreAdminHome/Controller.php
+++ b/plugins/CoreAdminHome/Controller.php
@@ -278,7 +278,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$successLogo = $logo->copyUploadedLogoToFilesystem();
$successFavicon = $logo->copyUploadedFaviconToFilesystem();
- if($successLogo || $successFavicon) {
+ if ($successLogo || $successFavicon) {
return '1';
}
return '0';
@@ -291,7 +291,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
private function saveGeneralSettings()
{
- if(!self::isGeneralSettingsAdminEnabled()) {
+ if (!self::isGeneralSettingsAdminEnabled()) {
// General settings + Beta channel + SMTP settings is disabled
return;
}
@@ -339,7 +339,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
{
// Whether to display or not the general settings (cron, beta, smtp)
$view->isGeneralSettingsAdminEnabled = self::isGeneralSettingsAdminEnabled();
- if($view->isGeneralSettingsAdminEnabled) {
+ if ($view->isGeneralSettingsAdminEnabled) {
$this->displayWarningIfConfigFileNotWritable();
}
diff --git a/plugins/CoreAdminHome/CustomLogo.php b/plugins/CoreAdminHome/CustomLogo.php
index fe7e78ac6e..eda7ef0638 100644
--- a/plugins/CoreAdminHome/CustomLogo.php
+++ b/plugins/CoreAdminHome/CustomLogo.php
@@ -88,7 +88,7 @@ class CustomLogo
*/
public function isCustomLogoWritable()
{
- if(Config::getInstance()->General['enable_custom_logo_check'] == 0) {
+ if (Config::getInstance()->General['enable_custom_logo_check'] == 0) {
return true;
}
$pathUserLogo = $this->getPathUserLogo();
@@ -196,7 +196,7 @@ class CustomLogo
$image = imagecreatefrompng($file);
break;
case 'image/gif':
- $image = imagecreatefromgif($file);
+ $image = imagecreatefromgif ($file);
break;
default:
return false;
diff --git a/plugins/CoreAdminHome/Tasks.php b/plugins/CoreAdminHome/Tasks.php
index 461ca5ca52..d633b9fd5d 100644
--- a/plugins/CoreAdminHome/Tasks.php
+++ b/plugins/CoreAdminHome/Tasks.php
@@ -35,7 +35,7 @@ class Tasks extends \Piwik\Plugin\Tasks
list($year, $month) = explode('_', $date);
// Somehow we may have archive tables created with older dates, prevent exception from being thrown
- if($year > 1990) {
+ if ($year > 1990) {
ArchivePurger::purgeOutdatedArchives(Date::factory("$year-$month-15"));
}
}
diff --git a/plugins/CoreConsole/Commands/GenerateCommand.php b/plugins/CoreConsole/Commands/GenerateCommand.php
index e8661f028e..9fe95c06d3 100644
--- a/plugins/CoreConsole/Commands/GenerateCommand.php
+++ b/plugins/CoreConsole/Commands/GenerateCommand.php
@@ -34,7 +34,9 @@ class GenerateCommand extends GeneratePluginBase
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExampleCommand';
$replace = array(
+ 'ExampleCommandDescription' => $commandName,
'ExampleCommand' => $pluginName,
+ 'examplecommand:helloworld' => strtolower($pluginName) . ':' . $this->buildCommandName($commandName),
'examplecommand' => strtolower($pluginName),
'HelloWorld' => $commandName,
'helloworld' => strtolower($commandName)
@@ -50,6 +52,15 @@ class GenerateCommand extends GeneratePluginBase
}
/**
+ * Convert MyComponentName => my-component-name
+ * @param string $commandNameCamelCase
+ * @return string
+ */
+ protected function buildCommandName($commandNameCamelCase)
+ {
+ return strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $commandNameCamelCase));
+ }
+ /**
* @param InputInterface $input
* @param OutputInterface $output
* @return string
@@ -64,12 +75,16 @@ class GenerateCommand extends GeneratePluginBase
throw new \InvalidArgumentException('You have to enter a command name');
}
+ if (!ctype_alnum($testname)) {
+ throw new \InvalidArgumentException('Only alphanumeric characters are allowed as a command name. Use CamelCase if the name of your command contains multiple words.');
+ }
+
return $testname;
};
if (empty($testname)) {
$dialog = $this->getHelperSet()->get('dialog');
- $testname = $dialog->askAndValidate($output, 'Enter the name of the command: ', $validate);
+ $testname = $dialog->askAndValidate($output, 'Enter the name of the command (CamelCase): ', $validate);
} else {
$validate($testname);
}
diff --git a/plugins/CoreConsole/Commands/GenerateTest.php b/plugins/CoreConsole/Commands/GenerateTest.php
index a9ace19a8b..ff68eec784 100644
--- a/plugins/CoreConsole/Commands/GenerateTest.php
+++ b/plugins/CoreConsole/Commands/GenerateTest.php
@@ -42,7 +42,7 @@ class GenerateTest extends GeneratePluginBase
);
$testClass = $this->getTestClass($testType);
- if(!empty($testClass)) {
+ if (!empty($testClass)) {
$replace['\PHPUnit_Framework_TestCase'] = $testClass;
}
@@ -167,7 +167,7 @@ class GenerateTest extends GeneratePluginBase
*/
protected function getTestFilesWhitelist($testType)
{
- if('Integration' == $testType) {
+ if ('Integration' == $testType) {
return array(
'/.gitignore',
'/tests',
diff --git a/plugins/CoreConsole/TravisYmlView.php b/plugins/CoreConsole/TravisYmlView.php
index cb4fabfda2..a89b5f1df5 100644
--- a/plugins/CoreConsole/TravisYmlView.php
+++ b/plugins/CoreConsole/TravisYmlView.php
@@ -89,6 +89,7 @@ class TravisYmlView extends View
$this->pluginName = $pluginName;
$customTravisBuildSteps = array();
+
foreach (self::$travisYmlExtendableSectionNames as $name) {
$customTravisBuildSteps[$name] = array();
@@ -102,6 +103,7 @@ class TravisYmlView extends View
$customTravisBuildSteps[$name]['after'] = $this->changeIndent(file_get_contents($afterStepsTemplate), ' ');
}
}
+
$this->customTravisBuildSteps = $customTravisBuildSteps;
}
@@ -160,12 +162,14 @@ class TravisYmlView extends View
preg_match_all("/^[a-zA-Z_]+:/m", $yamlText, $allMatches, PREG_OFFSET_CAPTURE);
$result = array();
+
foreach ($allMatches[0] as $match) {
$matchLength = strlen($match[0]);
$sectionName = substr($match[0], 0, $matchLength - 1);
$result[$sectionName] = $match[1] + $matchLength;
}
+
return $result;
}
@@ -206,6 +210,7 @@ class TravisYmlView extends View
$testsToExclude[] = array('php' => '5.4',
'env' => 'TEST_SUITE=PluginTests MYSQL_ADAPTER=PDO_MYSQL TEST_AGAINST_CORE=latest_stable');
}
+
if ($this->isTargetPluginContainsUITests()) {
$testsToRun[] = array('name' => 'UITests',
'vars' => "MYSQL_ADAPTER=PDO_MYSQL");
diff --git a/plugins/CoreHome/Columns/UserId.php b/plugins/CoreHome/Columns/UserId.php
index 146a7ea4c6..55fea6b517 100644
--- a/plugins/CoreHome/Columns/UserId.php
+++ b/plugins/CoreHome/Columns/UserId.php
@@ -48,12 +48,7 @@ class UserId extends VisitDimension
*/
public function onExistingVisit(Request $request, Visitor $visitor, $action)
{
- $forcedUserId = $request->getForcedUserId();
- if ($forcedUserId) {
- return $forcedUserId;
- }
-
- return false;
+ return $request->getForcedUserId();
}
} \ No newline at end of file
diff --git a/plugins/CoreHome/Columns/VisitGoalBuyer.php b/plugins/CoreHome/Columns/VisitGoalBuyer.php
index 1fcf91cb96..8867efd708 100644
--- a/plugins/CoreHome/Columns/VisitGoalBuyer.php
+++ b/plugins/CoreHome/Columns/VisitGoalBuyer.php
@@ -72,7 +72,7 @@ class VisitGoalBuyer extends VisitDimension
// Ecommerce buyer status
$visitEcommerceStatus = $this->getBuyerType($request, $goalBuyer);
- if($visitEcommerceStatus != self::TYPE_BUYER_NONE
+ if ($visitEcommerceStatus != self::TYPE_BUYER_NONE
// only update if the value has changed (prevents overwriting the value in case a request has
// updated it in the meantime)
&& $visitEcommerceStatus != $goalBuyer) {
diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php
index b0d3770cbe..387c31e9e9 100644
--- a/plugins/CoreHome/Controller.php
+++ b/plugins/CoreHome/Controller.php
@@ -103,9 +103,11 @@ class Controller extends \Piwik\Plugin\Controller
) {
$module = 'MultiSites';
}
+
if ($defaultReport == Piwik::getLoginPluginName()) {
$module = Piwik::getLoginPluginName();
}
+
$idSite = Common::getRequestVar('idSite', false, 'int');
parent::redirectToIndex($module, $action, $idSite);
}
@@ -113,10 +115,12 @@ class Controller extends \Piwik\Plugin\Controller
public function showInContext()
{
$controllerName = Common::getRequestVar('moduleToLoad');
- $actionName = Common::getRequestVar('actionToLoad', 'index');
+ $actionName = Common::getRequestVar('actionToLoad', 'index');
+
if ($actionName == 'showInContext') {
throw new Exception("Preventing infinite recursion...");
}
+
$view = $this->getDefaultIndexView();
$view->content = FrontController::getInstance()->fetchDispatch($controllerName, $actionName);
return $view->render();
@@ -146,12 +150,16 @@ class Controller extends \Piwik\Plugin\Controller
) {
return;
}
+
$websiteId = Common::getRequestVar('idSite', false, 'int');
+
if ($websiteId) {
+
$website = new Site($websiteId);
- $datetimeCreationDate = $website->getCreationDate()->getDatetime();
+ $datetimeCreationDate = $website->getCreationDate()->getDatetime();
$creationDateLocalTimezone = Date::factory($datetimeCreationDate, $website->getTimezone())->toString('Y-m-d');
- $todayLocalTimezone = Date::factory('now', $website->getTimezone())->toString('Y-m-d');
+ $todayLocalTimezone = Date::factory('now', $website->getTimezone())->toString('Y-m-d');
+
if ($creationDateLocalTimezone == $todayLocalTimezone) {
Piwik::redirectToModule('CoreHome', 'index',
array('date' => 'today',
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index 187f356c5e..f833b1e5cc 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -69,6 +69,7 @@ class CoreHome extends \Piwik\Plugin
$stylesheets[] = "plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.directive.less";
$stylesheets[] = "plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less";
$stylesheets[] = "plugins/CoreHome/angularjs/dialogtoggler/ngdialog.less";
+ $stylesheets[] = "plugins/CoreHome/angularjs/notification/notification.directive.less";
}
public function getJsFiles(&$jsFiles)
@@ -149,6 +150,9 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler.directive.js";
$jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler.controller.js";
$jsFiles[] = "plugins/CoreHome/angularjs/dialogtoggler/dialogtoggler-urllistener.service.js";
+
+ $jsFiles[] = "plugins/CoreHome/angularjs/notification/notification.controller.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/notification/notification.directive.js";
}
public function getClientSideTranslationKeys(&$translationKeys)
diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
index 915139d300..b0922f05bf 100644
--- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php
+++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
@@ -86,7 +86,7 @@ class RowEvolution
if (empty($this->apiMethod)) throw new Exception("Parameter apiMethod not set.");
$this->label = ResponseBuilder::getLabelFromRequest($_GET);
- if(!is_array($this->label)) {
+ if (!is_array($this->label)) {
throw new Exception("Expected label to be an array, got instead: " . $this->label);
}
$this->label = $this->label[0];
@@ -337,7 +337,7 @@ class RowEvolution
$labelPretty = $dataTableMap->getColumn('label_html');
$labelPretty = array_filter($labelPretty, 'strlen');
$labelPretty = current($labelPretty);
- if(!empty($labelPretty)) {
+ if (!empty($labelPretty)) {
return $labelPretty;
}
return $rowLabel;
diff --git a/plugins/CoreHome/Visitor.php b/plugins/CoreHome/Visitor.php
index 688d1c8bb6..35b64b3d30 100644
--- a/plugins/CoreHome/Visitor.php
+++ b/plugins/CoreHome/Visitor.php
@@ -102,10 +102,10 @@ class Visitor
function getUserId()
{
if (isset($this->details['user_id'])
- && !is_null($this->details['user_id'])) {
+ && strlen($this->details['user_id']) > 0) {
return $this->details['user_id'];
}
- return false;
+ return null;
}
} \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/directives/translate.js b/plugins/CoreHome/angularjs/common/directives/translate.js
index 4cc20e83b3..5a9588f7c6 100644
--- a/plugins/CoreHome/angularjs/common/directives/translate.js
+++ b/plugins/CoreHome/angularjs/common/directives/translate.js
@@ -12,6 +12,8 @@
* within the sprintf args. Using the filter, this is not possible w/o manually sanitizing
* and creating trusted HTML, which is not as safe.
*
+ * Note: nesting this directive is not supported.
+ *
* Usage:
* <span piwik-translate="Plugin_TranslationToken">
* first arg::<strong>second arg</strong>::{{ unsafeDataThatWillBeSanitized }}
@@ -22,10 +24,8 @@
function piwikTranslate() {
return {
+ priority: 1,
restrict: 'A',
- scope: {
- piwikTranslate: '@'
- },
compile: function(element, attrs) {
var parts = element.html().split('::'),
translated = _pk_translate(attrs.piwikTranslate, parts);
diff --git a/plugins/CoreHome/angularjs/common/services/piwik-api.js b/plugins/CoreHome/angularjs/common/services/piwik-api.js
index 7b15c48fa8..80ac84312d 100644
--- a/plugins/CoreHome/angularjs/common/services/piwik-api.js
+++ b/plugins/CoreHome/angularjs/common/services/piwik-api.js
@@ -44,7 +44,9 @@
}
function createResponseErrorNotification(response, options) {
- if (response.message) {
+ if (response.message
+ && options.createErrorNotification
+ ) {
var UI = require('piwik/UI');
var notification = new UI.Notification();
notification.show(response.message, {
@@ -66,6 +68,10 @@
options = {};
}
+ if (options.createErrorNotification === undefined) {
+ options.createErrorNotification = true;
+ }
+
var deferred = $q.defer(),
requestPromise = deferred.promise;
@@ -271,7 +277,7 @@
* @deprecated
*/
abort: abort,
- abortAll: abortAll,
+ abortAll: abortAll
};
}
})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/notification/notification.controller.js b/plugins/CoreHome/angularjs/notification/notification.controller.js
new file mode 100644
index 0000000000..645cc13e59
--- /dev/null
+++ b/plugins/CoreHome/angularjs/notification/notification.controller.js
@@ -0,0 +1,34 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('NotificationController', NotificationController);
+
+ NotificationController.$inject = ['piwikApi'];
+
+ function NotificationController(piwikApi) {
+ /**
+ * Marks a persistent notification as read so it will not reappear on the next page
+ * load.
+ */
+ this.markNotificationAsRead = function () {
+ var notificationId = this.notificationId;
+ if (!notificationId) {
+ return;
+ }
+
+ piwikApi.post(
+ { // GET params
+ module: 'CoreHome',
+ action: 'markNotificationAsRead'
+ },
+ { // POST params
+ notificationId: notificationId
+ }
+ );
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.html b/plugins/CoreHome/angularjs/notification/notification.directive.html
new file mode 100644
index 0000000000..a643be8a3b
--- /dev/null
+++ b/plugins/CoreHome/angularjs/notification/notification.directive.html
@@ -0,0 +1,8 @@
+<div class="notification system">
+ <button type="button" class="close" data-dismiss="alert" ng-if="!noclear" ng-click="notification.markNotificationAsRead()">&times;</button>
+ <strong ng-if="title">{{ title }}</strong>
+
+ <!-- ng-transclude causes directive child elements to be added here -->
+ <div ng-transclude style="display:inline-block"></div>
+
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.js b/plugins/CoreHome/angularjs/notification/notification.directive.js
new file mode 100644
index 0000000000..167c61575b
--- /dev/null
+++ b/plugins/CoreHome/angularjs/notification/notification.directive.js
@@ -0,0 +1,95 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Directive to show a notification.
+ *
+ * Note: using this directive is preferred over the Notification class (which uses jquery
+ * exclusively).
+ *
+ * Supports the following attributes:
+ *
+ * * **context**: Either 'success', 'error', 'info', 'warning'
+ * * **type**: Either 'toast', 'persistent', 'transient'
+ * * **noclear**: If truthy, no clear button is displayed. For persistent notifications, has no effect.
+ *
+ * Usage:
+ *
+ * <div piwik-notification context="success" type="persistent" noclear="true">
+ * <strong>Info: </strong>My notification message.
+ * </div>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikNotification', piwikNotification);
+
+ piwikNotification.$inject = ['piwik', '$timeout'];
+
+ function piwikNotification(piwik, $timeout) {
+ return {
+ restrict: 'A',
+ scope: {
+ notificationId: '@?',
+ title: '@?notificationTitle', // TODO: shouldn't need this since the title can be specified within
+ // HTML of the node that uses the directive.
+ context: '@?',
+ type: '@?',
+ noclear: '=?'
+ },
+ transclude: true,
+ templateUrl: 'plugins/CoreHome/angularjs/notification/notification.directive.html?cb=' + piwik.cacheBuster,
+ controller: 'NotificationController',
+ controllerAs: 'notification',
+ link: function (scope, element, attrs) {
+ if (scope.context) {
+ element.children('.notification').addClass('notification-' + scope.context);
+ }
+
+ if (scope.type == 'persistent') {
+ // otherwise it is never possible to dismiss the notification
+ scope.noclear = false;
+ }
+
+ if (scope.notificationId) {
+ closeExistingNotificationHavingSameIdIfNeeded();
+ }
+
+ if ('toast' == scope.type) {
+ addToastEvent();
+ }
+
+ if (!scope.noclear) {
+ addCloseEvent();
+ }
+
+ function closeExistingNotificationHavingSameIdIfNeeded() {
+ // TODO: instead of doing a global query for notification, there should be a notification-container
+ // directive that manages notifications.
+ var existingNode = angular.element('.system.notification[notification-id=' + attrs.id + ']');
+ if (existingNode && existingNode.length) {
+ existingNode.remove();
+ }
+ }
+
+ function addToastEvent() {
+ $timeout(function () {
+ element.fadeOut('slow', function() {
+ element.remove();
+ });
+ }, 12 * 1000);
+ }
+
+ function addCloseEvent() {
+ element.on('click', '.close', function (event) {
+ if (event && event.delegateTarget) {
+ angular.element(event.delegateTarget).remove();
+ }
+ });
+ }
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/notification/notification.directive.less b/plugins/CoreHome/angularjs/notification/notification.directive.less
new file mode 100644
index 0000000000..bc3c5e3f5f
--- /dev/null
+++ b/plugins/CoreHome/angularjs/notification/notification.directive.less
@@ -0,0 +1,84 @@
+.system.notification {
+ color: #9b7a44;
+ float: none;
+
+ padding: 15px 35px 15px 15px;
+ text-shadow: 0 1px 0 rgba(255,255,255,.5);
+ background-color: #ffffe0;
+ border: 1px solid #e6db55;
+ border-radius: 3px;
+ font-size: 14px;
+ margin: 0px;
+ margin-bottom: 16px;
+
+ a {
+ color: #9b7a44;
+ text-decoration: underline;
+ }
+
+ .close {
+ position: relative;
+ top: -5px;
+ right: -28px;
+ line-height: 20px;
+ }
+
+ button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+ }
+ .close {
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+ }
+
+ &.notification-success {
+ background-color: #dff0d8;
+ border-color: #c3d6b7;
+ color: #468847;
+
+ a {
+ color: #468847
+ }
+ }
+ &.notification-danger,
+ &.notification-error {
+ background-color: #f2dede;
+ border-color: #d5bfc4;
+ color: #b94a48;
+
+ a {
+ color: #b94a48
+ }
+ }
+ &.notification-info {
+ background-color: #d9edf7;
+ border-color: #a7d3e3;
+ color: #3a87ad;
+
+ a {
+ color: #3a87ad
+ }
+ }
+
+ &.notification-block {
+ padding-top: 14px;
+ padding-bottom: 14px;
+ }
+ &.notification-block > p,
+ &.notification-block > ul {
+ margin-bottom: 0;
+ }
+ &.notification-block p + p {
+ margin-top: 5px;
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/javascripts/notification.js b/plugins/CoreHome/javascripts/notification.js
index 7bd92042a5..266477446e 100644
--- a/plugins/CoreHome/javascripts/notification.js
+++ b/plugins/CoreHome/javascripts/notification.js
@@ -50,26 +50,8 @@
options = {};
}
- if ('persistent' == options.type) {
- // otherwise it is never possible to dismiss the notification
- options.noclear = false;
- }
-
- closeExistingNotificationHavingSameIdIfNeeded(options);
-
- var template = generateNotificationHtmlMarkup(options, message);
- var $notificationNode = placeNotification(template, options);
- this.$node = $notificationNode;
-
- if ('persistent' == options.type) {
- addPersistentEvent($notificationNode);
- } else if ('toast' == options.type) {
- addToastEvent($notificationNode);
- }
-
- if (!options.noclear) {
- addCloseEvent($notificationNode);
- }
+ var template = generateNotificationHtmlMarkup(options, message);
+ this.$node = placeNotification(template, options);
};
Notification.prototype.scrollToNotification = function () {
@@ -80,69 +62,37 @@
exports.Notification = Notification;
- function closeExistingNotificationHavingSameIdIfNeeded(options)
- {
- if (!options.id) {
- return;
- }
-
- var $existingNode = $('.system.notification[data-id=' + options.id + ']');
- if ($existingNode && $existingNode.length) {
- $existingNode.remove();
- }
- }
-
function generateNotificationHtmlMarkup(options, message) {
- var template = buildNotificationStart(options);
-
- if (!options.noclear) {
- template += buildClearButton();
- }
-
- if (options.title) {
- template += buildTitle(options);
- }
-
- template += message;
- template += buildNotificationEnd();
-
- return template;
- }
-
- function buildNotificationStart(options) {
- var template = '<div class="notification system';
-
- if (options.context) {
- template += ' notification-' + options.context;
- }
-
- template += '"';
-
- if (options.id) {
- template += ' data-id="' + options.id + '"';
+ var attributeMapping = {
+ id: 'notification-id',
+ title: 'notification-title',
+ context: 'context',
+ type: 'type',
+ noclear: 'noclear'
+ },
+ html = '<div piwik-notification';
+
+ for (var key in attributeMapping) {
+ if (attributeMapping.hasOwnProperty(key)
+ && options[key]
+ ) {
+ html += ' ' + attributeMapping[key] + '="' + options[key].toString().replace(/"/g, "&quot;") + '"';
+ }
}
- template += '>';
-
- return template;
- }
-
- function buildNotificationEnd() {
- return '</div>';
- }
-
- function buildClearButton() {
- return '<button type="button" class="close" data-dismiss="alert">&times;</button>';
- }
+ html += '>' + message + '</div>';
- function buildTitle(options) {
- return '<strong>' + options.title + '</strong> ';
+ return html;
}
function placeNotification(template, options) {
-
var $notificationNode = $(template);
+ // compile the template in angular
+ angular.element(document).injector().invoke(function ($compile, $rootScope) {
+ $compile($notificationNode)($rootScope.$new(true));
+ });
+
if (options.style) {
$notificationNode.css(options.style);
}
@@ -158,41 +108,4 @@
return $notificationNode;
}
-
- function addToastEvent($notificationNode)
- {
- setTimeout(function () {
- $notificationNode.fadeOut( 'slow', function() {
- $notificationNode.remove();
- $notificationNode = null;
- });
- }, 12 * 1000);
- }
-
- function addCloseEvent($notificationNode) {
- $notificationNode.on('click', '.close', function (event) {
- if (event && event.delegateTarget) {
- $(event.delegateTarget).remove();
- }
- });
- };
-
- function addPersistentEvent($notificationNode) {
- var notificationId = $notificationNode.data('id');
-
- if (!notificationId) {
- return;
- }
-
- $notificationNode.on('click', '.close', function (event) {
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'CoreHome',
- action: 'markNotificationAsRead'
- }, 'GET');
- ajaxHandler.addParams({notificationId: notificationId}, 'POST');
- ajaxHandler.send(true);
- });
- };
-
})(jQuery, require); \ No newline at end of file
diff --git a/plugins/CoreHome/stylesheets/notification.less b/plugins/CoreHome/stylesheets/notification.less
index 23b29e1fe4..36f7085f6f 100644
--- a/plugins/CoreHome/stylesheets/notification.less
+++ b/plugins/CoreHome/stylesheets/notification.less
@@ -5,89 +5,4 @@
.notification {
margin: 10px;
}
-}
-
-.system.notification {
- color: #9b7a44;
- float: none;
-
- padding: 15px 35px 15px 15px;
- text-shadow: 0 1px 0 rgba(255,255,255,.5);
- background-color: #ffffe0;
- border: 1px solid #e6db55;
- border-radius: 3px;
- font-size: 14px;
- margin: 0px;
- margin-bottom: 16px;
-
- a {
- color: #9b7a44;
- text-decoration: underline;
- }
-
- .close {
- position: relative;
- top: -5px;
- right: -28px;
- line-height: 20px;
- }
-
- button.close {
- padding: 0;
- cursor: pointer;
- background: transparent;
- border: 0;
- -webkit-appearance: none;
- }
- .close {
- float: right;
- font-size: 20px;
- font-weight: bold;
- line-height: 20px;
- color: #000000;
- text-shadow: 0 1px 0 #ffffff;
- opacity: 0.2;
- filter: alpha(opacity=20);
- }
-
- &.notification-success {
- background-color: #dff0d8;
- border-color: #c3d6b7;
- color: #468847;
-
- a {
- color: #468847
- }
- }
- &.notification-danger,
- &.notification-error {
- background-color: #f2dede;
- border-color: #d5bfc4;
- color: #b94a48;
-
- a {
- color: #b94a48
- }
- }
- &.notification-info {
- background-color: #d9edf7;
- border-color: #a7d3e3;
- color: #3a87ad;
-
- a {
- color: #3a87ad
- }
- }
-
- &.notification-block {
- padding-top: 14px;
- padding-bottom: 14px;
- }
- &.notification-block > p,
- &.notification-block > ul {
- margin-bottom: 0;
- }
- &.notification-block p + p {
- margin-top: 5px;
- }
-}
+} \ No newline at end of file
diff --git a/plugins/CoreHome/templates/_topBarTopMenu.twig b/plugins/CoreHome/templates/_topBarTopMenu.twig
index 5f5831237a..fc09b62f78 100644
--- a/plugins/CoreHome/templates/_topBarTopMenu.twig
+++ b/plugins/CoreHome/templates/_topBarTopMenu.twig
@@ -36,17 +36,8 @@
menu-title="{{ helloAlias|trim }}"
piwik-menudropdown>
- {% if userLogin != 'anonymous' %}
- {% if isAdminLayout is defined and currentModule == 'UsersManager' and currentAction == 'userSettings' %}
- <a class="item active" href="index.php?module=CoreAdminHome">{{ 'General_Settings'|translate }}</a>
- {% else %}
- <a class="item" href="index.php?module=CoreAdminHome">{{ 'General_Settings'|translate }}</a>
- {% endif %}
- {% endif %}
-
{% for lev1UserLabel,lev1UserMenu in userMenu if lev1UserLabel|slice(0,1) != '_' %}
-
- {% if userLogin != 'anonymous' or not loop.first %}
+ {% if not loop.first %}
<hr class="item separator"/>
{% endif %}
diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php
index 01c2837857..70942c7715 100644
--- a/plugins/CorePluginsAdmin/Controller.php
+++ b/plugins/CorePluginsAdmin/Controller.php
@@ -293,7 +293,7 @@ class Controller extends Plugin\ControllerAdmin
$suffix = Piwik::translate('CorePluginsAdmin_PluginNotWorkingAlternative');
// If the plugin has been renamed, we do not show message to ask user to update plugin
- if($pluginName != Request::renameModule($pluginName)) {
+ if ($pluginName != Request::renameModule($pluginName)) {
$suffix = "You may uninstall the plugin or manually delete the files in piwik/plugins/$pluginName/";
}
@@ -359,7 +359,7 @@ class Controller extends Plugin\ControllerAdmin
return $message;
}
- if(Common::isPhpCliMode()) {
+ if (Common::isPhpCliMode()) {
Piwik_ExitWithMessage("Error:" . var_export($lastError, true));
}
diff --git a/plugins/CorePluginsAdmin/PluginInstaller.php b/plugins/CorePluginsAdmin/PluginInstaller.php
index 58fd074909..8bb9c475d0 100644
--- a/plugins/CorePluginsAdmin/PluginInstaller.php
+++ b/plugins/CorePluginsAdmin/PluginInstaller.php
@@ -155,7 +155,7 @@ class PluginInstaller
private function makeSureThereAreNoMissingRequirements($metadata)
{
$requires = array();
- if(!empty($metadata->require)) {
+ if (!empty($metadata->require)) {
$requires = (array) $metadata->require;
}
diff --git a/plugins/CorePluginsAdmin/UpdateCommunication.php b/plugins/CorePluginsAdmin/UpdateCommunication.php
index ad6c570e7e..3ed004993f 100644
--- a/plugins/CorePluginsAdmin/UpdateCommunication.php
+++ b/plugins/CorePluginsAdmin/UpdateCommunication.php
@@ -109,34 +109,8 @@ class UpdateCommunication
$hasPluginUpdate = $hasPluginUpdate || !$plugin['isTheme'];
}
- $subject = Piwik::translate('CoreUpdater_NotificationSubjectAvailablePluginUpdate');
- $message = Piwik::translate('ScheduledReports_EmailHello');
- $message .= "\n\n";
- $message .= Piwik::translate('CoreUpdater_ThereIsNewPluginVersionAvailableForUpdate');
- $message .= "\n\n";
-
- foreach ($pluginsToBeNotified as $plugin) {
- $message .= sprintf(' * %s %s', $plugin['name'], $plugin['latestVersion']);
- $message .= "\n";
- }
-
- $message .= "\n";
-
- $host = SettingsPiwik::getPiwikUrl();
- if ($hasThemeUpdate) {
- $message .= Piwik::translate('CoreUpdater_NotificationClickToUpdateThemes') . "\n";
- $message .= $host. 'index.php?module=CorePluginsAdmin&action=themes';
- }
- if ($hasPluginUpdate) {
- if ($hasThemeUpdate) {
- $message .= "\n\n";
- }
- $message .= Piwik::translate('CoreUpdater_NotificationClickToUpdatePlugins') . "\n";
- $message .= $host. 'index.php?module=CorePluginsAdmin&action=plugins';
- }
-
- $message .= "\n\n";
- $message .= Piwik::translate('Installation_HappyAnalysing');
+ $subject = Piwik::translate('CoreUpdater_NotificationSubjectAvailablePluginUpdate');
+ $message = $this->buildNotificationMessage($pluginsToBeNotified, $hasThemeUpdate, $hasPluginUpdate);
$this->sendEmailNotification($subject, $message);
}
@@ -161,24 +135,24 @@ class UpdateCommunication
}
}
- private function setHasLatestUpdateNotificationReceived($plugin)
+ protected function setHasLatestUpdateNotificationReceived($plugin)
{
$latestVersion = $this->getLatestVersion($plugin);
Option::set($this->getNotificationSentOptionName($plugin), $latestVersion);
}
- private function getLatestVersionSent($plugin)
+ protected function getLatestVersionSent($plugin)
{
return Option::get($this->getNotificationSentOptionName($plugin));
}
- private function getLatestVersion($plugin)
+ protected function getLatestVersion($plugin)
{
return $plugin['latestVersion'];
}
- private function hasNotificationAlreadyReceived($plugin)
+ protected function hasNotificationAlreadyReceived($plugin)
{
$latestVersion = $this->getLatestVersion($plugin);
$lastVersionSent = $this->getLatestVersionSent($plugin);
@@ -192,7 +166,7 @@ class UpdateCommunication
return false;
}
- private function getNotificationSentOptionName($plugin)
+ protected function getNotificationSentOptionName($plugin)
{
return 'last_update_communication_sent_plugin_' . $plugin['name'];
}
@@ -207,4 +181,39 @@ class UpdateCommunication
return $plugins;
}
+
+ protected function buildNotificationMessage($pluginsToBeNotified, $hasThemeUpdate, $hasPluginUpdate)
+ {
+ $message = Piwik::translate('ScheduledReports_EmailHello');
+ $message .= "\n\n";
+ $message .= Piwik::translate('CoreUpdater_ThereIsNewPluginVersionAvailableForUpdate');
+ $message .= "\n\n";
+
+ foreach ($pluginsToBeNotified as $plugin) {
+ $message .= sprintf(' * %s %s', $plugin['name'], $plugin['latestVersion']);
+ $message .= "\n";
+ }
+
+ $message .= "\n";
+
+ $host = SettingsPiwik::getPiwikUrl();
+
+ if ($hasThemeUpdate) {
+ $message .= Piwik::translate('CoreUpdater_NotificationClickToUpdateThemes') . "\n";
+ $message .= $host . 'index.php?module=CorePluginsAdmin&action=themes';
+ }
+
+ if ($hasPluginUpdate) {
+ if ($hasThemeUpdate) {
+ $message .= "\n\n";
+ }
+ $message .= Piwik::translate('CoreUpdater_NotificationClickToUpdatePlugins') . "\n";
+ $message .= $host . 'index.php?module=CorePluginsAdmin&action=plugins';
+ }
+
+ $message .= "\n\n";
+ $message .= Piwik::translate('Installation_HappyAnalysing');
+
+ return $message;
+ }
}
diff --git a/plugins/CoreUpdater/Commands/Update.php b/plugins/CoreUpdater/Commands/Update.php
index e5a0e39d33..108281c460 100644
--- a/plugins/CoreUpdater/Commands/Update.php
+++ b/plugins/CoreUpdater/Commands/Update.php
@@ -40,7 +40,7 @@ class Update extends ConsoleCommand
try {
$this->makeUpdate($input, $output, $doDryRun);
- if(!$doDryRun) {
+ if (!$doDryRun) {
$this->writeSuccessMessage($output, array("Piwik has been successfully updated!"));
}
diff --git a/plugins/CoreUpdater/Controller.php b/plugins/CoreUpdater/Controller.php
index 1d1d689b56..cc27c3e9aa 100644
--- a/plugins/CoreUpdater/Controller.php
+++ b/plugins/CoreUpdater/Controller.php
@@ -327,11 +327,11 @@ class Controller extends \Piwik\Plugin\Controller
$doExecuteUpdates = Common::getRequestVar('updateCorePlugins', 0, 'integer') == 1;
- if(is_null($doDryRun)) {
+ if (is_null($doDryRun)) {
$doDryRun = !$doExecuteUpdates;
}
- if($doDryRun) {
+ if ($doDryRun) {
$viewWelcome->queries = $updater->getSqlQueriesToExecute();
$viewWelcome->isMajor = $updater->hasMajorDbUpdate();
$this->doWelcomeUpdates($viewWelcome, $componentsWithUpdateFile);
diff --git a/plugins/CoreUpdater/UpdateCommunication.php b/plugins/CoreUpdater/UpdateCommunication.php
index 5a113f74d5..cd52e80a3e 100644
--- a/plugins/CoreUpdater/UpdateCommunication.php
+++ b/plugins/CoreUpdater/UpdateCommunication.php
@@ -76,7 +76,7 @@ class UpdateCommunication
$this->sendEmailNotification($subject, $message);
}
- private function isVersionLike($latestVersion)
+ protected function isVersionLike($latestVersion)
{
return strlen($latestVersion) < 18;
}
@@ -101,7 +101,7 @@ class UpdateCommunication
}
}
- private function isNewVersionAvailable()
+ protected function isNewVersionAvailable()
{
UpdateCheck::check();
@@ -119,7 +119,7 @@ class UpdateCommunication
return $hasUpdate;
}
- private function hasNotificationAlreadyReceived()
+ protected function hasNotificationAlreadyReceived()
{
$latestVersion = $this->getLatestVersion();
$lastVersionSent = $this->getLatestVersionSent();
diff --git a/plugins/CustomVariables/Archiver.php b/plugins/CustomVariables/Archiver.php
index 241e2772a0..47e1363dc4 100644
--- a/plugins/CustomVariables/Archiver.php
+++ b/plugins/CustomVariables/Archiver.php
@@ -39,7 +39,7 @@ class Archiver extends \Piwik\Plugin\Archiver
{
parent::__construct($processor);
- if($processor->getParams()->getSite()->isEcommerceEnabled()) {
+ if ($processor->getParams()->getSite()->isEcommerceEnabled()) {
$this->maximumRowsInDataTableLevelZero = self::MAX_ROWS_WHEN_ECOMMERCE;
$this->maximumRowsInSubDataTable = self::MAX_ROWS_WHEN_ECOMMERCE;
} else {
diff --git a/plugins/CustomVariables/tests/ModelTest.php b/plugins/CustomVariables/tests/ModelTest.php
index bfca81c777..3ed82f552a 100644
--- a/plugins/CustomVariables/tests/ModelTest.php
+++ b/plugins/CustomVariables/tests/ModelTest.php
@@ -7,16 +7,36 @@
*/
namespace Piwik\Plugins\CustomVariables\tests;
+use Piwik\Common;
use Piwik\Db;
+use Piwik\DbHelper;
use Piwik\Plugins\CustomVariables\Model;
/**
* @group CustomVariables
* @group ModelTest
* @group Database
+ * @group CustomVariables_ModelTest
*/
class ModelTest extends \DatabaseTestCase
{
+ private static $cvarScopes = array('log_link_visit_action', 'log_visit', 'log_conversion');
+
+ public function setUp()
+ {
+ // do not call parent::setUp() since it expects database to be created,
+ // but DB for this test is removed in tearDown
+
+ self::$fixture->performSetUp();
+ }
+
+ public function tearDown()
+ {
+ parent::tearDown();
+
+ self::$fixture->performTearDown();
+ }
+
/**
* @expectedException \Exception
*/
@@ -35,7 +55,7 @@ class ModelTest extends \DatabaseTestCase
public function testGetAllScopes()
{
- $this->assertEquals(array('log_link_visit_action', 'log_visit', 'log_conversion'), Model::getScopes());
+ $this->assertEquals(self::$cvarScopes, Model::getScopes());
}
public function test_Install_Uninstall()
diff --git a/plugins/CustomVariables/tests/expected/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml b/plugins/CustomVariables/tests/expected/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml
index 6626b1a24d..2e53cbf4d8 100644
--- a/plugins/CustomVariables/tests/expected/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml
+++ b/plugins/CustomVariables/tests/expected/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml
@@ -69,7 +69,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/plugins/CustomVariables/tests/processed/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml b/plugins/CustomVariables/tests/processed/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml
index 6626b1a24d..2e53cbf4d8 100644
--- a/plugins/CustomVariables/tests/processed/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml
+++ b/plugins/CustomVariables/tests/processed/test_CustomVariablesIntegrationTest__Live.getLastVisitsDetails_day.xml
@@ -69,7 +69,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/plugins/Dashboard/Controller.php b/plugins/Dashboard/Controller.php
index 5e319ddb25..c7a89a75a5 100644
--- a/plugins/Dashboard/Controller.php
+++ b/plugins/Dashboard/Controller.php
@@ -104,38 +104,13 @@ class Controller extends \Piwik\Plugin\Controller
$session->dashboardLayout = $layout;
$session->setExpirationSeconds(1800);
} else {
- $this->saveLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout);
+ $this->getModel()->updateLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout);
}
}
- /**
- * Records the layout in the DB for the given user.
- *
- * @param string $login
- * @param int $idDashboard
- * @param string $layout
- */
- protected function saveLayoutForUser($login, $idDashboard, $layout)
- {
- $paramsBind = array($login, $idDashboard, $layout, $layout);
- $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?',
- Common::prefixTable('user_dashboard'));
- Db::query($query, $paramsBind);
- }
-
- /**
- * Updates the name of a dashboard
- *
- * @param string $login
- * @param int $idDashboard
- * @param string $name
- */
- protected function updateDashboardName($login, $idDashboard, $name)
+ private function getModel()
{
- $paramsBind = array($name, $login, $idDashboard);
- $query = sprintf('UPDATE %s SET name = ? WHERE login = ? AND iddashboard = ?',
- Common::prefixTable('user_dashboard'));
- Db::query($query, $paramsBind);
+ return new Model();
}
/**
@@ -153,9 +128,7 @@ class Controller extends \Piwik\Plugin\Controller
// first layout can't be removed
if ($idDashboard != 1) {
- $query = sprintf('DELETE FROM %s WHERE iddashboard = ? AND login = ?',
- Common::prefixTable('user_dashboard'));
- Db::query($query, array($idDashboard, Piwik::getCurrentUserLogin()));
+ $this->getModel()->deleteDashboardForUser($idDashboard, Piwik::getCurrentUserLogin());
}
}
@@ -171,7 +144,7 @@ class Controller extends \Piwik\Plugin\Controller
return '[]';
}
- $login = Piwik::getCurrentUserLogin();
+ $login = Piwik::getCurrentUserLogin();
$dashboards = $this->dashboard->getAllDashboards($login);
Json::sendHeaderJSON();
@@ -189,38 +162,22 @@ class Controller extends \Piwik\Plugin\Controller
if (Piwik::isUserIsAnonymous()) {
return '0';
}
- $user = Piwik::getCurrentUserLogin();
- $nextId = $this->getNextIdDashboard($user);
- $name = urldecode(Common::getRequestVar('name', '', 'string'));
- $type = urldecode(Common::getRequestVar('type', 'default', 'string'));
+ $name = urldecode(Common::getRequestVar('name', '', 'string'));
+ $type = urldecode(Common::getRequestVar('type', 'default', 'string'));
$layout = '{}';
+ $login = Piwik::getCurrentUserLogin();
if ($type == 'default') {
$layout = $this->dashboard->getDefaultLayout();
}
- $query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)',
- Common::prefixTable('user_dashboard'));
- Db::query($query, array($user, $nextId, $name, $layout));
+ $nextId = $this->getModel()->createNewDashboardForUser($login, $name, $layout);
Json::sendHeaderJSON();
return Common::json_encode($nextId);
}
- private function getNextIdDashboard($login)
- {
- $nextIdQuery = sprintf('SELECT MAX(iddashboard)+1 FROM %s WHERE login = ?',
- Common::prefixTable('user_dashboard'));
- $nextId = Db::fetchOne($nextIdQuery, array($login));
-
- if (empty($nextId)) {
- $nextId = 1;
- return $nextId;
- }
- return $nextId;
- }
-
public function copyDashboardToUser()
{
$this->checkTokenInUrl();
@@ -228,18 +185,16 @@ class Controller extends \Piwik\Plugin\Controller
if (!Piwik::hasUserSuperUserAccess()) {
return '0';
}
+
$login = Piwik::getCurrentUserLogin();
- $name = urldecode(Common::getRequestVar('name', '', 'string'));
- $user = urldecode(Common::getRequestVar('user', '', 'string'));
+ $name = urldecode(Common::getRequestVar('name', '', 'string'));
+ $user = urldecode(Common::getRequestVar('user', '', 'string'));
$idDashboard = Common::getRequestVar('dashboardId', 0, 'int');
+
$layout = $this->dashboard->getLayoutForUser($login, $idDashboard);
if ($layout !== false) {
- $nextId = $this->getNextIdDashboard($user);
-
- $query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)',
- Common::prefixTable('user_dashboard'));
- Db::query($query, array($user, $nextId, $name, $layout));
+ $nextId = $this->getModel()->createNewDashboardForUser($user, $name, $layout);
Json::sendHeaderJSON();
return Common::json_encode($nextId);
@@ -255,17 +210,18 @@ class Controller extends \Piwik\Plugin\Controller
{
$this->checkTokenInUrl();
- $layout = Common::unsanitizeInputValue(Common::getRequestVar('layout'));
+ $layout = Common::unsanitizeInputValue(Common::getRequestVar('layout'));
$idDashboard = Common::getRequestVar('idDashboard', 1, 'int');
- $name = Common::getRequestVar('name', '', 'string');
+ $name = Common::getRequestVar('name', '', 'string');
+
if (Piwik::isUserIsAnonymous()) {
$session = new SessionNamespace("Dashboard");
$session->dashboardLayout = $layout;
$session->setExpirationSeconds(1800);
} else {
- $this->saveLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout);
+ $this->getModel()->updateLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout);
if (!empty($name)) {
- $this->updateDashboardName(Piwik::getCurrentUserLogin(), $idDashboard, $name);
+ $this->getModel()->updateDashboardName(Piwik::getCurrentUserLogin(), $idDashboard, $name);
}
}
}
@@ -279,10 +235,7 @@ class Controller extends \Piwik\Plugin\Controller
if (Piwik::hasUserSuperUserAccess()) {
$layout = Common::unsanitizeInputValue(Common::getRequestVar('layout'));
- $paramsBind = array('', '1', $layout, $layout);
- $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?',
- Common::prefixTable('user_dashboard'));
- Db::query($query, $paramsBind);
+ $this->getModel()->createOrUpdateDashboard('', '1', $layout);
}
}
diff --git a/plugins/Dashboard/Dashboard.php b/plugins/Dashboard/Dashboard.php
index cb545465ff..1ff5a98182 100644
--- a/plugins/Dashboard/Dashboard.php
+++ b/plugins/Dashboard/Dashboard.php
@@ -10,7 +10,6 @@ namespace Piwik\Plugins\Dashboard;
use Piwik\Common;
use Piwik\Db;
-use Piwik\DbHelper;
use Piwik\Piwik;
use Piwik\WidgetsList;
@@ -42,10 +41,7 @@ class Dashboard extends \Piwik\Plugin
*/
public function getLayoutForUser($login, $idDashboard)
{
- $paramsBind = array($login, $idDashboard);
- $query = sprintf('SELECT layout FROM %s WHERE login = ? AND iddashboard = ?',
- Common::prefixTable('user_dashboard'));
- $return = Db::fetchAll($query, $paramsBind);
+ $return = $this->getModel()->getLayoutForUser($login, $idDashboard);
if (count($return) == 0) {
return false;
@@ -54,6 +50,11 @@ class Dashboard extends \Piwik\Plugin
return $return[0]['layout'];
}
+ private function getModel()
+ {
+ return new Model();
+ }
+
public function getDefaultLayout()
{
$defaultLayout = $this->getLayoutForUser('', 1);
@@ -107,9 +108,7 @@ class Dashboard extends \Piwik\Plugin
public function getAllDashboards($login)
{
- $dashboards = Db::fetchAll('SELECT iddashboard, name, layout
- FROM ' . Common::prefixTable('user_dashboard') .
- ' WHERE login = ? ORDER BY iddashboard', array($login));
+ $dashboards = $this->getModel()->getAllDashboardsForUser($login);
$nameless = 1;
foreach ($dashboards as &$dashboard) {
@@ -219,23 +218,17 @@ class Dashboard extends \Piwik\Plugin
public function deleteDashboardLayout($userLogin)
{
- Db::query('DELETE FROM ' . Common::prefixTable('user_dashboard') . ' WHERE login = ?', array($userLogin));
+ $this->getModel()->deleteAllLayoutsForUser($userLogin);
}
public function install()
{
- $dashboard = "login VARCHAR( 100 ) NOT NULL ,
- iddashboard INT NOT NULL ,
- name VARCHAR( 100 ) NULL DEFAULT NULL ,
- layout TEXT NOT NULL,
- PRIMARY KEY ( login , iddashboard )";
-
- DbHelper::createTable('user_dashboard', $dashboard);
+ Model::install();
}
public function uninstall()
{
- Db::dropTables(Common::prefixTable('user_dashboard'));
+ Model::uninstall();
}
public function getClientSideTranslationKeys(&$translationKeys)
diff --git a/plugins/Dashboard/Menu.php b/plugins/Dashboard/Menu.php
index 2ee0521623..f90c117e10 100644
--- a/plugins/Dashboard/Menu.php
+++ b/plugins/Dashboard/Menu.php
@@ -41,11 +41,9 @@ class Menu extends \Piwik\Plugin\Menu
{
$userPreferences = new UserPreferences();
$idSite = $userPreferences->getDefaultWebsiteId();
-
$tooltip = Piwik::translate('Dashboard_TopLinkTooltip', Site::getNameFor($idSite));
- $urlParams = $this->urlForModuleAction('CoreHome', 'index', array('idSite' => $idSite)) ;
-
+ $urlParams = $this->urlForModuleActionWithDefaultUserParams('CoreHome', 'index') ;
$menu->addItem('Dashboard_Dashboard', null, $urlParams, 1, $tooltip);
}
}
diff --git a/plugins/Dashboard/Model.php b/plugins/Dashboard/Model.php
new file mode 100644
index 0000000000..39dcb06557
--- /dev/null
+++ b/plugins/Dashboard/Model.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+namespace Piwik\Plugins\Dashboard;
+
+use Piwik\Common;
+use Piwik\Db;
+use Piwik\DbHelper;
+
+class Model
+{
+ private static $rawPrefix = 'user_dashboard';
+ private $table;
+
+ public function __construct()
+ {
+ $this->table = Common::prefixTable(self::$rawPrefix);
+ }
+
+ /**
+ * Returns the layout in the DB for the given user, or false if the layout has not been set yet.
+ * Parameters must be checked BEFORE this function call
+ *
+ * @param string $login
+ * @param int $idDashboard
+ *
+ * @return bool|string
+ */
+ public function getLayoutForUser($login, $idDashboard)
+ {
+ $query = sprintf('SELECT layout FROM %s WHERE login = ? AND iddashboard = ?',
+ $this->table);
+ $bind = array($login, $idDashboard);
+ $layouts = Db::fetchAll($query, $bind);
+
+ return $layouts;
+ }
+
+ public function getAllDashboardsForUser($login)
+ {
+ $dashboards = Db::fetchAll('SELECT iddashboard, name, layout FROM ' . $this->table .
+ ' WHERE login = ? ORDER BY iddashboard', array($login));
+
+ return $dashboards;
+ }
+
+ public function deleteAllLayoutsForUser($userLogin)
+ {
+ Db::query('DELETE FROM ' . $this->table . ' WHERE login = ?', array($userLogin));
+ }
+
+ /**
+ * Updates the name of a dashboard
+ *
+ * @param string $login
+ * @param int $idDashboard
+ * @param string $name
+ */
+ public function updateDashboardName($login, $idDashboard, $name)
+ {
+ $bind = array($name, $login, $idDashboard);
+ $query = sprintf('UPDATE %s SET name = ? WHERE login = ? AND iddashboard = ?', $this->table);
+ Db::query($query, $bind);
+ }
+
+ /**
+ * Removes the dashboard with the given id
+ */
+ public function deleteDashboardForUser($idDashboard, $login)
+ {
+ $query = sprintf('DELETE FROM %s WHERE iddashboard = ? AND login = ?', $this->table);
+ Db::query($query, array($idDashboard, $login));
+ }
+
+ /**
+ * Creates a new dashboard for the current user
+ * User needs to be logged in
+ */
+ public function createNewDashboardForUser($login, $name, $layout)
+ {
+ $nextId = $this->getNextIdDashboard($login);
+
+ $query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)', $this->table);
+ $bind = array($login, $nextId, $name, $layout);
+ Db::query($query, $bind);
+
+ return $nextId;
+ }
+
+ /**
+ * Saves the layout as default
+ */
+ public function createOrUpdateDashboard($login, $idDashboard, $layout)
+ {
+ $bind = array($login, $idDashboard, $layout, $layout);
+ $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?',
+ $this->table);
+ Db::query($query, $bind);
+ }
+
+ private function getNextIdDashboard($login)
+ {
+ $nextIdQuery = sprintf('SELECT MAX(iddashboard)+1 FROM %s WHERE login = ?', $this->table);
+ $nextId = Db::fetchOne($nextIdQuery, array($login));
+
+ if (empty($nextId)) {
+ $nextId = 1;
+ }
+
+ return $nextId;
+ }
+
+ /**
+ * Records the layout in the DB for the given user.
+ *
+ * @param string $login
+ * @param int $idDashboard
+ * @param string $layout
+ */
+ public function updateLayoutForUser($login, $idDashboard, $layout)
+ {
+ $bind = array($login, $idDashboard, $layout, $layout);
+ $query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?',
+ $this->table);
+ Db::query($query, $bind);
+ }
+
+ public static function install()
+ {
+ $dashboard = "login VARCHAR( 100 ) NOT NULL ,
+ iddashboard INT NOT NULL ,
+ name VARCHAR( 100 ) NULL DEFAULT NULL ,
+ layout TEXT NOT NULL,
+ PRIMARY KEY ( login , iddashboard )";
+
+ DbHelper::createTable(self::$rawPrefix, $dashboard);
+ }
+
+ public static function uninstall()
+ {
+ Db::dropTables(Common::prefixTable(self::$rawPrefix));
+ }
+}
diff --git a/plugins/DevicesDetection/functions.php b/plugins/DevicesDetection/functions.php
index 1112be3050..12f18b19d1 100644
--- a/plugins/DevicesDetection/functions.php
+++ b/plugins/DevicesDetection/functions.php
@@ -179,7 +179,7 @@ function getOSFamilyFullNameExtended($label)
return 'Bot';
}
$label = OperatingSystemParser::getOsFamily($label);
- if($label !== false) {
+ if ($label !== false) {
return $label;
}
return Piwik::translate('General_Unknown');
@@ -292,4 +292,4 @@ function getBrowserEngineName($engineName) {
return $engineName;
}
return Piwik::translate('General_Unknown');
-} \ No newline at end of file
+}
diff --git a/plugins/Events/API.php b/plugins/Events/API.php
index 361f0ef965..890979db2c 100644
--- a/plugins/Events/API.php
+++ b/plugins/Events/API.php
@@ -94,7 +94,7 @@ class API extends \Piwik\Plugin\API
*/
public function getDefaultSecondaryDimension($apiMethod)
{
- if(isset($this->defaultMappingApiToSecondaryDimension[$apiMethod])) {
+ if (isset($this->defaultMappingApiToSecondaryDimension[$apiMethod])) {
return $this->defaultMappingApiToSecondaryDimension[$apiMethod];
}
return false;
@@ -106,11 +106,11 @@ class API extends \Piwik\Plugin\API
$secondaryDimension = $this->getDefaultSecondaryDimension($apiMethod);
}
$record = $this->mappingApiToRecord[$apiMethod];
- if(!is_array($record)) {
+ if (!is_array($record)) {
return $record;
}
// when secondaryDimension is incorrectly set
- if(empty($record[$secondaryDimension])) {
+ if (empty($record[$secondaryDimension])) {
return key($record);
}
return $record[$secondaryDimension];
@@ -124,7 +124,7 @@ class API extends \Piwik\Plugin\API
public function getSecondaryDimensions($apiMethod)
{
$records = $this->mappingApiToRecord[$apiMethod];
- if(!is_array($records)) {
+ if (!is_array($records)) {
return false;
}
return array_keys($records);
diff --git a/plugins/Events/Archiver.php b/plugins/Events/Archiver.php
index 02fe3a8d83..eb33d4899f 100644
--- a/plugins/Events/Archiver.php
+++ b/plugins/Events/Archiver.php
@@ -225,7 +225,7 @@ class Archiver extends \Piwik\Plugin\Archiver
*/
protected function getDataArray($name)
{
- if(empty($this->arrays[$name])) {
+ if (empty($this->arrays[$name])) {
$this->arrays[$name] = new DataArray();
}
return $this->arrays[$name];
diff --git a/plugins/Events/Events.php b/plugins/Events/Events.php
index 3d335bd229..6a55588af4 100644
--- a/plugins/Events/Events.php
+++ b/plugins/Events/Events.php
@@ -184,7 +184,7 @@ class Events extends \Piwik\Plugin
private function addRelatedReports($view, $secondaryDimension)
{
- if(empty($secondaryDimension)) {
+ if (empty($secondaryDimension)) {
// eg. Row Evolution
return;
}
@@ -194,7 +194,7 @@ class Events extends \Piwik\Plugin
$apiMethod = $view->requestConfig->getApiMethodToRequest();
$secondaryDimensions = API::getInstance()->getSecondaryDimensions($apiMethod);
- if(empty($secondaryDimensions)) {
+ if (empty($secondaryDimensions)) {
return;
}
@@ -205,7 +205,7 @@ class Events extends \Piwik\Plugin
. Piwik::translate('Events_SwitchToSecondaryDimension', '');
foreach($secondaryDimensions as $dimension) {
- if($dimension == $secondaryDimension) {
+ if ($dimension == $secondaryDimension) {
// don't show as related report the currently selected dimension
continue;
}
diff --git a/plugins/ExampleCommand/Commands/HelloWorld.php b/plugins/ExampleCommand/Commands/HelloWorld.php
index 5c0d415dcd..d8dc781e3f 100644
--- a/plugins/ExampleCommand/Commands/HelloWorld.php
+++ b/plugins/ExampleCommand/Commands/HelloWorld.php
@@ -15,18 +15,36 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
+ * This class lets you define a new command. To read more about commands have a look at our Piwik Console guide on
+ * http://developer.piwik.org/guides/piwik-on-the-command-line
+ *
+ * As Piwik Console is based on the Symfony Console you might also want to have a look at
+ * http://symfony.com/doc/current/components/console/index.html
*/
class HelloWorld extends ConsoleCommand
{
+
+ /**
+ * This methods allows you to configure your command. Here you can define the name and description of your command
+ * as well as all options and arguments you expect when executing it.
+ */
protected function configure()
{
$this->setName('examplecommand:helloworld');
- $this->setDescription('ExampleCommand');
+ $this->setDescription('ExampleCommandDescription');
$this->addOption('name', null, InputOption::VALUE_REQUIRED, 'Your name:');
}
/**
- * Execute command like: ./console examplecommand:helloworld --name="The Piwik Team"
+ * The actual task is defined in this method. Here you can access any option or argument that was defined on the
+ * command line via $input and write anything to the console via $output argument.
+ * In case anything went wrong during the execution you should throw an exception to make sure the user will get a
+ * useful error message and to make sure the command does not exit with the status code 0.
+ *
+ * Ideally, the actual command is quite short as it acts like a controller. It should only receive the input values,
+ * execute the task by calling a method of another class and output any useful information.
+ *
+ * Execute the command like: ./console examplecommand:helloworld --name="The Piwik Team"
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
diff --git a/plugins/ExampleRssWidget/RssRenderer.php b/plugins/ExampleRssWidget/RssRenderer.php
index 254a30ce6f..7990b1acb8 100644
--- a/plugins/ExampleRssWidget/RssRenderer.php
+++ b/plugins/ExampleRssWidget/RssRenderer.php
@@ -54,7 +54,7 @@ class RssRenderer
$i = 0;
$items = array();
- if(!empty($rss->channel->item)) {
+ if (!empty($rss->channel->item)) {
$items = $rss->channel->item;
}
foreach ($items as $post) {
diff --git a/plugins/ExampleUI/templates/notifications.twig b/plugins/ExampleUI/templates/notifications.twig
index 2212ead3d9..1058a50a61 100644
--- a/plugins/ExampleUI/templates/notifications.twig
+++ b/plugins/ExampleUI/templates/notifications.twig
@@ -4,6 +4,11 @@
<h2>Inline notification example:</h2>
<div style="display:inline-block;margin-top:10px;" id="exampleUI_notifications">
- {{ 'This is an example for an inline notification. Have you noticed the success message disappeared after a few seconds?'|notification({'placeAt': '#exampleUI_notifications', 'title': 'Info: ', 'noclear': true, 'context': 'info'}) }}
+ <div piwik-notification
+ notification-title="Info:"
+ noclear="true"
+ context="info">
+ This is an example for an inline notification. Have you noticed the success message disappeared after a few seconds?
+ </div>
</div>
{% endblock %} \ No newline at end of file
diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php
index 176cafad24..c76054aab9 100644
--- a/plugins/Goals/API.php
+++ b/plugins/Goals/API.php
@@ -54,14 +54,15 @@ class API extends \Piwik\Plugin\API
//TODO calls to this function could be cached as static
// would help UI at least, since some UI requests would call this 2-3 times..
$idSite = Site::getIdSitesFromIdSitesString($idSite);
+
if (empty($idSite)) {
return array();
}
+
Piwik::checkUserHasViewAccess($idSite);
- $goals = Db::fetchAll("SELECT *
- FROM " . Common::prefixTable('goal') . "
- WHERE idsite IN (" . implode(", ", $idSite) . ")
- AND deleted = 0");
+
+ $goals = $this->getModel()->getActiveGoals($idSite);
+
$cleanedGoals = array();
foreach ($goals as &$goal) {
if ($goal['match_attribute'] == 'manually') {
@@ -71,6 +72,7 @@ class API extends \Piwik\Plugin\API
}
$cleanedGoals[$goal['idgoal']] = $goal;
}
+
return $cleanedGoals;
}
@@ -91,35 +93,33 @@ class API extends \Piwik\Plugin\API
public function addGoal($idSite, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
{
Piwik::checkUserHasAdminAccess($idSite);
+
$this->checkPatternIsValid($patternType, $pattern, $matchAttribute);
- $name = $this->checkName($name);
+ $name = $this->checkName($name);
$pattern = $this->checkPattern($pattern);
- // save in db
- $db = Db::get();
- $idGoal = $db->fetchOne("SELECT max(idgoal) + 1
- FROM " . Common::prefixTable('goal') . "
- WHERE idsite = ?", $idSite);
- if ($idGoal == false) {
- $idGoal = 1;
- }
- $db->insert(Common::prefixTable('goal'),
- array(
- 'idsite' => $idSite,
- 'idgoal' => $idGoal,
- 'name' => $name,
- 'match_attribute' => $matchAttribute,
- 'pattern' => $pattern,
- 'pattern_type' => $patternType,
- 'case_sensitive' => (int)$caseSensitive,
- 'allow_multiple' => (int)$allowMultipleConversionsPerVisit,
- 'revenue' => (float)$revenue,
- 'deleted' => 0,
- ));
+ $goal = array(
+ 'name' => $name,
+ 'match_attribute' => $matchAttribute,
+ 'pattern' => $pattern,
+ 'pattern_type' => $patternType,
+ 'case_sensitive' => (int)$caseSensitive,
+ 'allow_multiple' => (int)$allowMultipleConversionsPerVisit,
+ 'revenue' => (float)$revenue,
+ 'deleted' => 0,
+ );
+
+ $idGoal = $this->getModel()->createGoalForSite($idSite, $goal);
+
Cache::regenerateCacheWebsiteAttributes($idSite);
return $idGoal;
}
+ private function getModel()
+ {
+ return new Model();
+ }
+
/**
* Updates a Goal description.
* Will not update or re-process the conversions already recorded
@@ -139,21 +139,21 @@ class API extends \Piwik\Plugin\API
public function updateGoal($idSite, $idGoal, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
{
Piwik::checkUserHasAdminAccess($idSite);
- $name = $this->checkName($name);
+
+ $name = $this->checkName($name);
$pattern = $this->checkPattern($pattern);
$this->checkPatternIsValid($patternType, $pattern, $matchAttribute);
- Db::get()->update(Common::prefixTable('goal'),
- array(
- 'name' => $name,
- 'match_attribute' => $matchAttribute,
- 'pattern' => $pattern,
- 'pattern_type' => $patternType,
- 'case_sensitive' => (int)$caseSensitive,
- 'allow_multiple' => (int)$allowMultipleConversionsPerVisit,
- 'revenue' => (float)$revenue,
- ),
- "idsite = '$idSite' AND idgoal = '$idGoal'"
- );
+
+ $this->getModel()->updateGoal($idSite, $idGoal, array(
+ 'name' => $name,
+ 'match_attribute' => $matchAttribute,
+ 'pattern' => $pattern,
+ 'pattern_type' => $patternType,
+ 'case_sensitive' => (int) $caseSensitive,
+ 'allow_multiple' => (int) $allowMultipleConversionsPerVisit,
+ 'revenue' => (float) $revenue,
+ ));
+
Cache::regenerateCacheWebsiteAttributes($idSite);
}
@@ -188,12 +188,10 @@ class API extends \Piwik\Plugin\API
public function deleteGoal($idSite, $idGoal)
{
Piwik::checkUserHasAdminAccess($idSite);
- Db::query("UPDATE " . Common::prefixTable('goal') . "
- SET deleted = 1
- WHERE idsite = ?
- AND idgoal = ?",
- array($idSite, $idGoal));
- Db::deleteAllRows(Common::prefixTable("log_conversion"), "WHERE idgoal = ? AND idsite = ?", "idvisit", 100000, array($idGoal, $idSite));
+
+ $this->getModel()->deleteGoal($idSite, $idGoal);
+ $this->getModel()->deleteGoalConversions($idSite, $idGoal);
+
Cache::regenerateCacheWebsiteAttributes($idSite);
}
@@ -204,11 +202,13 @@ class API extends \Piwik\Plugin\API
protected function getItems($recordName, $idSite, $period, $date, $abandonedCarts, $segment)
{
Piwik::checkUserHasViewAccess($idSite);
+
$recordNameFinal = $recordName;
if ($abandonedCarts) {
$recordNameFinal = Archiver::getItemRecordNameAbandonedCart($recordName);
}
- $archive = Archive::build($idSite, $period, $date, $segment);
+
+ $archive = Archive::build($idSite, $period, $date, $segment);
$dataTable = $archive->getDataTable($recordNameFinal);
$dataTable->filter('Sort', array(Metrics::INDEX_ECOMMERCE_ITEM_REVENUE));
@@ -250,6 +250,7 @@ class API extends \Piwik\Plugin\API
}
return;
}
+
$rowNotDefined = $dataTable->getRowFromLabel(\Piwik\Plugins\CustomVariables\Archiver::LABEL_CUSTOM_VALUE_NOT_DEFINED);
if ($rowNotDefined) {
$rowNotDefined->setColumn('label', $notDefinedStringPretty);
diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php
index f5a3f1f735..e7af6d6243 100644
--- a/plugins/Goals/Controller.php
+++ b/plugins/Goals/Controller.php
@@ -98,7 +98,7 @@ class Controller extends \Piwik\Plugin\Controller
{
$saveGET = $_GET;
$filterEcommerce = Common::getRequestVar('filterEcommerce', self::ECOMMERCE_LOG_SHOW_ORDERS, 'int');
- if($filterEcommerce == self::ECOMMERCE_LOG_SHOW_ORDERS) {
+ if ($filterEcommerce == self::ECOMMERCE_LOG_SHOW_ORDERS) {
$segment = urlencode('visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart');
} else {
$segment = urlencode('visitEcommerceStatus==abandonedCart,visitEcommerceStatus==orderedThenAbandonedCart');
@@ -458,7 +458,7 @@ class Controller extends \Piwik\Plugin\Controller
foreach ($allReports as $category => $reports) {
$categoryText = Piwik::translate('Goals_ViewGoalsBy', $category);
foreach ($reports as $report) {
- if(empty($report['viewDataTable'])) {
+ if (empty($report['viewDataTable'])) {
$report['viewDataTable'] = 'tableGoals';
}
$customParams['viewDataTable'] = $report['viewDataTable'];
diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php
index 39a7c0b2dc..381c37f7ee 100644
--- a/plugins/Goals/Goals.php
+++ b/plugins/Goals/Goals.php
@@ -135,7 +135,8 @@ class Goals extends \Piwik\Plugin
*/
public function deleteSiteGoals($idSite)
{
- Db::query("DELETE FROM " . Common::prefixTable('goal') . " WHERE idsite = ? ", array($idSite));
+ $model = new Model();
+ $model->deleteGoalsForSite($idSite);
}
/**
diff --git a/plugins/Goals/Model.php b/plugins/Goals/Model.php
new file mode 100644
index 0000000000..2b35f7947d
--- /dev/null
+++ b/plugins/Goals/Model.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Goals;
+
+use Piwik\Common;
+use Piwik\Db;
+
+class Model
+{
+ private static $rawPrefix = 'goal';
+ private $table;
+
+ public function __construct()
+ {
+ $this->table = Common::prefixTable(self::$rawPrefix);
+ }
+
+ private function getNextIdGoal($idSite)
+ {
+ $db = $this->getDb();
+ $idGoal = $db->fetchOne("SELECT max(idgoal) + 1 FROM " . $this->table . "
+ WHERE idsite = ?", $idSite);
+
+ if (empty($idGoal)) {
+ $idGoal = 1;
+ }
+
+ return $idGoal;
+ }
+
+ public function createGoalForSite($idSite, $goal)
+ {
+ $db = $this->getDb();
+ $goalId = $this->getNextIdGoal($idSite);
+
+ $goal['idgoal'] = $goalId;
+ $goal['idsite'] = $idSite;
+
+ $db->insert($this->table, $goal);
+
+ return $goalId;
+ }
+
+ public function updateGoal($idSite, $idGoal, $goal)
+ {
+ $idSite = (int) $idSite;
+ $idGoal = (int) $idGoal;
+
+ $db = $this->getDb();
+ $db->update($this->table, $goal, "idsite = '$idSite' AND idgoal = '$idGoal'");
+ }
+
+ // actually this should be in a log_conversion model
+ public function deleteGoalConversions($idSite, $idGoal)
+ {
+ $table = Common::prefixTable("log_conversion");
+
+ Db::deleteAllRows($table, "WHERE idgoal = ? AND idsite = ?", "idvisit", 100000, array($idGoal, $idSite));
+ }
+
+ public function getActiveGoals($idSite)
+ {
+ $idSite = array_map('intval', $idSite);
+ $goals = Db::fetchAll("SELECT * FROM " . $this->table . "
+ WHERE idsite IN (" . implode(", ", $idSite) . ")
+ AND deleted = 0");
+
+ return $goals;
+ }
+
+ public function deleteGoalsForSite($idSite)
+ {
+ Db::query("DELETE FROM " . $this->table . " WHERE idsite = ? ", array($idSite));
+ }
+
+ public function deleteGoal($idSite, $idGoal)
+ {
+ $query = "UPDATE " . $this->table . " SET deleted = 1
+ WHERE idsite = ? AND idgoal = ?";
+ $bind = array($idSite, $idGoal);
+
+ Db::query($query, $bind);
+ }
+
+ private function getDb()
+ {
+ return Db::get();
+ }
+}
diff --git a/plugins/Goals/Visualizations/Goals.php b/plugins/Goals/Visualizations/Goals.php
index 0b8c2c5365..9808621b14 100644
--- a/plugins/Goals/Visualizations/Goals.php
+++ b/plugins/Goals/Visualizations/Goals.php
@@ -31,7 +31,7 @@ class Goals extends HtmlTable
{
parent::beforeLoadDataTable();
- if($this->config->disable_subtable_when_show_goals) {
+ if ($this->config->disable_subtable_when_show_goals) {
$this->config->subtable_controller_action = null;
}
diff --git a/plugins/Goals/tests/APITest.php b/plugins/Goals/tests/APITest.php
index 0f393385d9..3263bc4972 100644
--- a/plugins/Goals/tests/APITest.php
+++ b/plugins/Goals/tests/APITest.php
@@ -8,6 +8,7 @@
namespace Piwik\Plugins\Goals\tests;
use Piwik\Access;
+use Piwik\Piwik;
use Piwik\Plugins\Goals\API;
use Piwik\Tests\Fixture;
@@ -31,6 +32,9 @@ class APITest extends \DatabaseTestCase
parent::setUp();
$this->api = API::getInstance();
+ Fixture::createAccessInstance();
+ Piwik::setUserHasSuperUserAccess();
+
Fixture::createWebsite('2014-01-01 00:00:00');
Fixture::createWebsite('2014-01-01 00:00:00');
}
diff --git a/plugins/ImageGraph/StaticGraph/GridGraph.php b/plugins/ImageGraph/StaticGraph/GridGraph.php
index 3eeb1393db..4ffde4c2c4 100644
--- a/plugins/ImageGraph/StaticGraph/GridGraph.php
+++ b/plugins/ImageGraph/StaticGraph/GridGraph.php
@@ -419,11 +419,11 @@ abstract class GridGraph extends StaticGraph
// see https://github.com/piwik/piwik/issues/3396
// protected function displayMinMaxValues()
// {
-// if($displayMinMax)
+// if ($displayMinMax)
// {
// // when plotting multiple metrics, display min & max on both series
// // to fix: in vertical bars, labels are hidden when multiple metrics are plotted, hence the restriction on count($this->ordinateSeries) == 1
-// if($this->multipleMetrics && count($this->ordinateSeries) == 1)
+// if ($this->multipleMetrics && count($this->ordinateSeries) == 1)
// {
// $colorIndex = 1;
// foreach($this->ordinateSeries as $column => $data)
@@ -467,13 +467,13 @@ abstract class GridGraph extends StaticGraph
// $maxValueIndex = 0;
// foreach($data as $index => $value)
// {
-// if($value > $maxValue)
+// if ($value > $maxValue)
// {
// $maxValue = $value;
// $maxValueIndex = $index;
// }
//
-// if($value < $minValue)
+// if ($value < $minValue)
// {
// $minValue = $value;
// $minValueIndex = $index;
diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php
index d3da5402f0..574af47ad7 100644
--- a/plugins/Installation/Controller.php
+++ b/plugins/Installation/Controller.php
@@ -84,7 +84,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
// Delete merged js/css files to force regenerations based on updated activated plugin list
Filesystem::deleteAllCacheOnUpdate();
- if(empty($message)) {
+ if (empty($message)) {
$this->checkPiwikIsNotInstalled();
}
$view = new View(
@@ -196,14 +196,16 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$view->tablesInstalled = implode(', ', $tablesInstalled);
$view->someTablesInstalled = true;
- Access::getInstance();
- Piwik::setUserHasSuperUserAccess();
- if ($this->hasEnoughTablesToReuseDb($tablesInstalled) &&
- count(APISitesManager::getInstance()->getAllSitesId()) > 0 &&
- count(APIUsersManager::getInstance()->getUsers()) > 0
- ) {
- $view->showReuseExistingTables = true;
- }
+ $self = $this;
+ Access::doAsSuperUser(function () use ($self, $tablesInstalled, $view) {
+ Access::getInstance();
+ if ($self->hasEnoughTablesToReuseDb($tablesInstalled) &&
+ count(APISitesManager::getInstance()->getAllSitesId()) > 0 &&
+ count(APIUsersManager::getInstance()->getUsers()) > 0
+ ) {
+ $view->showReuseExistingTables = true;
+ }
+ });
} else {
DbHelper::createTables();
@@ -234,7 +236,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
);
$result = $this->updateComponents();
- if($result === false) {
+ if ($result === false) {
$this->redirectToNextStep('tablesCreation');
}
@@ -258,8 +260,11 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
{
$this->checkPiwikIsNotInstalled();
- $this->initObjectsToCallAPI();
- if(count(APIUsersManager::getInstance()->getUsersHavingSuperUserAccess()) > 0) {
+ $superUserAlreadyExists = Access::doAsSuperUser(function () {
+ return count(APIUsersManager::getInstance()->getUsersHavingSuperUserAccess()) > 0;
+ });
+
+ if ($superUserAlreadyExists) {
$this->redirectToNextStep('setupSuperUser');
}
@@ -301,9 +306,11 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
{
$this->checkPiwikIsNotInstalled();
- $this->initObjectsToCallAPI();
+ $siteIdsCount = Access::doAsSuperUser(function () {
+ return count(APISitesManager::getInstance()->getAllSitesId());
+ });
- if(count(APISitesManager::getInstance()->getAllSitesId()) > 0) {
+ if ($siteIdsCount > 0) {
// if there is a already a website, skip this step and trackingCode step
$this->redirectToNextStep('trackingCode');
}
@@ -317,12 +324,15 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$form = new FormFirstWebsiteSetup();
if ($form->validate()) {
- $name = Common::unsanitizeInputValue($form->getSubmitValue('siteName'));
+ $name = Common::sanitizeInputValue($form->getSubmitValue('siteName'));
$url = Common::unsanitizeInputValue($form->getSubmitValue('url'));
$ecommerce = (int)$form->getSubmitValue('ecommerce');
try {
- $result = APISitesManager::getInstance()->addSite($name, $url, $ecommerce);
+ $result = Access::doAsSuperUser(function () use ($name, $url, $ecommerce) {
+ return APISitesManager::getInstance()->addSite($name, $url, $ecommerce);
+ });
+
$params = array(
'site_idSite' => $result,
'site_name' => urlencode($name)
@@ -336,7 +346,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
}
// Display previous step success message, when current step form was not submitted yet
- if(count($form->getErrorMessages()) == 0) {
+ if (count($form->getErrorMessages()) == 0) {
$view->displayGeneralSetupSuccess = true;
}
@@ -464,14 +474,6 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
}
/**
- * Instantiate access and log objects
- */
- private function initObjectsToCallAPI()
- {
- Piwik::setUserHasSuperUserAccess();
- }
-
- /**
* Write configuration file from session-store
*/
private function createConfigFile($dbInfos)
@@ -513,7 +515,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
private function checkPiwikIsNotInstalled()
{
- if(!SettingsPiwik::isPiwikInstalled()) {
+ if (!SettingsPiwik::isPiwikInstalled()) {
return;
}
\Piwik\Plugins\Login\Controller::clearSession();
@@ -633,13 +635,12 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
private function createSuperUser($login, $password, $email)
{
- $this->initObjectsToCallAPI();
-
- $api = APIUsersManager::getInstance();
- $api->addUser($login, $password, $email);
-
- $this->initObjectsToCallAPI();
- $api->setSuperUserAccess($login, true);
+ $self = $this;
+ Access::doAsSuperUser(function () use ($self, $login, $password, $email) {
+ $api = APIUsersManager::getInstance();
+ $api->addUser($login, $password, $email);
+ $api->setSuperUserAccess($login, true);
+ });
}
private function hasEnoughTablesToReuseDb($tablesInstalled)
@@ -658,7 +659,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
private function deleteConfigFileIfNeeded()
{
$config = Config::getInstance();
- if($config->existsLocalConfig()) {
+ if ($config->existsLocalConfig()) {
$config->deleteLocalConfig();
}
}
@@ -702,16 +703,16 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
protected function updateComponents()
{
Access::getInstance();
- Piwik::setUserHasSuperUserAccess();
- $updater = new Updater();
- $componentsWithUpdateFile = CoreUpdater::getComponentUpdates($updater);
+ return Access::doAsSuperUser(function () {
+ $updater = new Updater();
+ $componentsWithUpdateFile = CoreUpdater::getComponentUpdates($updater);
- if (empty($componentsWithUpdateFile)) {
- return false;
- }
- $result = CoreUpdater::updateComponents($updater, $componentsWithUpdateFile);
- return $result;
+ if (empty($componentsWithUpdateFile)) {
+ return false;
+ }
+ $result = CoreUpdater::updateComponents($updater, $componentsWithUpdateFile);
+ return $result;
+ });
}
-
}
diff --git a/plugins/Installation/FormFirstWebsiteSetup.php b/plugins/Installation/FormFirstWebsiteSetup.php
index 83c68bea34..924c711a30 100644
--- a/plugins/Installation/FormFirstWebsiteSetup.php
+++ b/plugins/Installation/FormFirstWebsiteSetup.php
@@ -12,6 +12,8 @@ namespace Piwik\Plugins\Installation;
use HTML_QuickForm2_DataSource_Array;
use HTML_QuickForm2_Factory;
use HTML_QuickForm2_Rule;
+use Piwik\Log;
+use Piwik\Access;
use Piwik\Piwik;
use Piwik\Plugins\SitesManager\API;
use Piwik\QuickForm2;
@@ -31,7 +33,7 @@ class FormFirstWebsiteSetup extends QuickForm2
HTML_QuickForm2_Factory::registerRule('checkTimezone', 'Piwik\Plugins\Installation\Rule_isValidTimezone');
$urlExample = 'http://example.org';
- $javascriptOnClickUrlExample = "javascript:if(this.value=='$urlExample'){this.value='http://';} this.style.color='black';";
+ $javascriptOnClickUrlExample = "javascript:if (this.value=='$urlExample'){this.value='http://';} this.style.color='black';";
$timezones = API::getInstance()->getTimezonesList();
$timezones = array_merge(array('No timezone' => Piwik::translate('SitesManager_SelectACity')), $timezones);
@@ -79,7 +81,9 @@ class Rule_isValidTimezone extends HTML_QuickForm2_Rule
try {
$timezone = $this->owner->getValue();
if (!empty($timezone)) {
- API::getInstance()->setDefaultTimezone($timezone);
+ Access::doAsSuperUser(function () use ($timezone) {
+ API::getInstance()->setDefaultTimezone($timezone);
+ });
}
} catch (\Exception $e) {
return false;
diff --git a/plugins/Installation/ServerFilesGenerator.php b/plugins/Installation/ServerFilesGenerator.php
index 5ce2abf844..9551340e4a 100644
--- a/plugins/Installation/ServerFilesGenerator.php
+++ b/plugins/Installation/ServerFilesGenerator.php
@@ -18,7 +18,7 @@ class ServerFilesGenerator
*/
public static function createHtAccessFiles()
{
- if(!SettingsServer::isApache()) {
+ if (!SettingsServer::isApache()) {
return;
}
$denyAll = self::getDenyAllHtaccessContent();
diff --git a/plugins/Installation/SystemCheck.php b/plugins/Installation/SystemCheck.php
index f6d8c42e02..29f87f2415 100644
--- a/plugins/Installation/SystemCheck.php
+++ b/plugins/Installation/SystemCheck.php
@@ -140,7 +140,7 @@ class SystemCheck
}
$sessionAutoStarted = (int)ini_get('session.auto_start');
- if($sessionAutoStarted) {
+ if ($sessionAutoStarted) {
$infos['missing_desired_functions'][] = 'session.auto_start';
}
diff --git a/plugins/LanguagesManager/API.php b/plugins/LanguagesManager/API.php
index e95931ae87..e715770279 100644
--- a/plugins/LanguagesManager/API.php
+++ b/plugins/LanguagesManager/API.php
@@ -9,7 +9,6 @@
*/
namespace Piwik\Plugins\LanguagesManager;
-use Piwik\Common;
use Piwik\Db;
use Piwik\Filesystem;
use Piwik\Piwik;
@@ -229,12 +228,20 @@ class API extends \Piwik\Plugin\API
*/
public function getLanguageForUser($login)
{
- if($login == 'anonymous') {
+ if ($login == 'anonymous') {
return false;
}
+
Piwik::checkUserHasSuperUserAccessOrIsTheUser($login);
- return Db::fetchOne('SELECT language FROM ' . Common::prefixTable('user_language') .
- ' WHERE login = ? ', array($login));
+
+ $lang = $this->getModel()->getLanguageForUser($login);
+
+ return $lang;
+ }
+
+ private function getModel()
+ {
+ return new Model();
}
/**
@@ -248,15 +255,13 @@ class API extends \Piwik\Plugin\API
{
Piwik::checkUserHasSuperUserAccessOrIsTheUser($login);
Piwik::checkUserIsNotAnonymous();
+
if (!$this->isLanguageAvailable($languageCode)) {
return false;
}
- $paramsBind = array($login, $languageCode, $languageCode);
- Db::query('INSERT INTO ' . Common::prefixTable('user_language') .
- ' (login, language)
- VALUES (?,?)
- ON DUPLICATE KEY UPDATE language=?',
- $paramsBind);
+
+ $this->getModel()->setLanguageForUser($login, $languageCode);
+
return true;
}
diff --git a/plugins/LanguagesManager/Commands/CompareKeys.php b/plugins/LanguagesManager/Commands/CompareKeys.php
index 088bb492ae..821342a90c 100644
--- a/plugins/LanguagesManager/Commands/CompareKeys.php
+++ b/plugins/LanguagesManager/Commands/CompareKeys.php
@@ -62,7 +62,7 @@ class CompareKeys extends ConsoleCommand
{
if (!empty($englishFromOTrance[$category])) {
foreach ($englishFromOTrance[$category] as $key => $value) {
- if(!array_key_exists($category, $availableTranslations) || !array_key_exists($key, $availableTranslations[$category])) {
+ if (!array_key_exists($category, $availableTranslations) || !array_key_exists($key, $availableTranslations[$category])) {
$unnecessary[] = sprintf('%s_%s', $category, $key);
continue;
} else if (html_entity_decode($availableTranslations[$category][$key]) != html_entity_decode($englishFromOTrance[$category][$key])) {
@@ -73,7 +73,7 @@ class CompareKeys extends ConsoleCommand
}
if (!empty($availableTranslations[$category])) {
foreach ($availableTranslations[$category] as $key => $value) {
- if(!array_key_exists($category, $englishFromOTrance) || !array_key_exists($key, $englishFromOTrance[$category])) {
+ if (!array_key_exists($category, $englishFromOTrance) || !array_key_exists($key, $englishFromOTrance[$category])) {
$missing[] = sprintf('%s_%s', $category, $key);
continue;
}
diff --git a/plugins/LanguagesManager/LanguagesManager.php b/plugins/LanguagesManager/LanguagesManager.php
index 66fba008e5..6a0fcf10e1 100644
--- a/plugins/LanguagesManager/LanguagesManager.php
+++ b/plugins/LanguagesManager/LanguagesManager.php
@@ -14,7 +14,6 @@ use Piwik\Common;
use Piwik\Config;
use Piwik\Cookie;
use Piwik\Db;
-use Piwik\DbHelper;
use Piwik\Piwik;
use Piwik\Translate;
use Piwik\View;
@@ -102,7 +101,8 @@ class LanguagesManager extends \Piwik\Plugin
public function deleteUserLanguage($userLogin)
{
- Db::query('DELETE FROM ' . Common::prefixTable('user_language') . ' WHERE login = ?', $userLogin);
+ $model = new Model();
+ $model->deleteUserLanguage($userLogin);
}
/**
@@ -110,10 +110,7 @@ class LanguagesManager extends \Piwik\Plugin
*/
public function install()
{
- $userLanguage = "login VARCHAR( 100 ) NOT NULL ,
- language VARCHAR( 10 ) NOT NULL ,
- PRIMARY KEY ( login )";
- DbHelper::createTable('user_language', $userLanguage);
+ Model::install();
}
/**
@@ -121,7 +118,7 @@ class LanguagesManager extends \Piwik\Plugin
*/
public function uninstall()
{
- Db::dropTables(Common::prefixTable('user_language'));
+ Model::uninstall();
}
/**
diff --git a/plugins/LanguagesManager/Model.php b/plugins/LanguagesManager/Model.php
new file mode 100644
index 0000000000..e40452c134
--- /dev/null
+++ b/plugins/LanguagesManager/Model.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ *
+ */
+namespace Piwik\Plugins\LanguagesManager;
+
+use Piwik\Common;
+use Piwik\Db;
+use Piwik\DbHelper;
+
+class Model
+{
+ private static $rawPrefix = 'user_language';
+ private $table;
+
+ public function __construct()
+ {
+ $this->table = Common::prefixTable(self::$rawPrefix);
+ }
+
+ public function deleteUserLanguage($userLogin)
+ {
+ Db::query('DELETE FROM ' . $this->table . ' WHERE login = ?', $userLogin);
+ }
+
+ /**
+ * Returns the language for the user
+ *
+ * @param string $userLogin
+ * @return string
+ */
+ public function getLanguageForUser($userLogin)
+ {
+ return Db::fetchOne('SELECT language FROM ' . $this->table .
+ ' WHERE login = ? ', array($userLogin));
+ }
+
+ /**
+ * Sets the language for the user
+ *
+ * @param string $login
+ * @param string $languageCode
+ * @return bool
+ */
+ public function setLanguageForUser($login, $languageCode)
+ {
+ $query = 'INSERT INTO ' . $this->table .
+ ' (login, language) VALUES (?,?) ON DUPLICATE KEY UPDATE language=?';
+ $bind = array($login, $languageCode, $languageCode);
+ Db::query($query, $bind);
+
+ return true;
+ }
+
+ public static function install()
+ {
+ $userLanguage = "login VARCHAR( 100 ) NOT NULL ,
+ language VARCHAR( 10 ) NOT NULL ,
+ PRIMARY KEY ( login )";
+ DbHelper::createTable(self::$rawPrefix, $userLanguage);
+ }
+
+ public static function uninstall()
+ {
+ Db::dropTables(Common::prefixTable(self::$rawPrefix));
+ }
+}
diff --git a/plugins/Live/API.php b/plugins/Live/API.php
index c2acabb619..d15cca9e36 100644
--- a/plugins/Live/API.php
+++ b/plugins/Live/API.php
@@ -306,7 +306,7 @@ class API extends \Piwik\Plugin\API
$cities[$countryCode] = array();
}
$city = $visit->getColumn('city');
- if(!empty($city)) {
+ if (!empty($city)) {
$cities[$countryCode][] = $city;
}
}
@@ -325,7 +325,7 @@ class API extends \Piwik\Plugin\API
'nb_visits' => $nbVisits,
'flag' => \Piwik\Plugins\UserCountry\getFlagFromCode($countryCode),
'prettyName' => \Piwik\Plugins\UserCountry\countryTranslate($countryCode));
- if(!empty($cities[$countryCode])) {
+ if (!empty($cities[$countryCode])) {
$countryInfo['cities'] = array_unique($cities[$countryCode]);
}
$result['countries'][] = $countryInfo;
@@ -476,8 +476,7 @@ class API extends \Piwik\Plugin\API
$segment = new Segment($segment, $idSite);
$queryInfo = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy, $groupBy);
- $sql = "SELECT sub.idvisitor, sub.visit_last_action_time
- FROM ({$queryInfo['sql']}) as sub
+ $sql = "SELECT sub.idvisitor, sub.visit_last_action_time FROM ({$queryInfo['sql']}) as sub
WHERE $visitLastActionTimeCondition
LIMIT 1";
$bind = array_merge($queryInfo['bind'], array($visitLastActionTime));
@@ -589,7 +588,7 @@ class API extends \Piwik\Plugin\API
$visitorDetailsArray['serverTimestamp'] = $visitorDetailsArray['lastActionTimestamp'];
$dateTimeVisit = Date::factory($visitorDetailsArray['lastActionTimestamp'], $timezone);
- if($dateTimeVisit) {
+ if ($dateTimeVisit) {
$visitorDetailsArray['serverTimePretty'] = $dateTimeVisit->getLocalized('%time%');
$visitorDetailsArray['serverDatePretty'] = $dateTimeVisit->getLocalized(Piwik::translate('CoreHome_ShortDateFormat'));
}
@@ -703,8 +702,7 @@ class API extends \Piwik\Plugin\API
// Group by idvisit so that a visitor converting 2 goals only appears once
$sql = "
- SELECT sub.*
- FROM (
+ SELECT sub.* FROM (
" . $subQuery['sql'] . "
$sqlLimit
) AS sub
diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php
index ae01ed4e18..55fd3fed83 100644
--- a/plugins/Live/Visitor.php
+++ b/plugins/Live/Visitor.php
@@ -309,7 +309,7 @@ class Visitor implements VisitorInterface
} elseif ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) {
// Handle Event
- if(strlen($actionDetail['pageTitle']) > 0) {
+ if (strlen($actionDetail['pageTitle']) > 0) {
$actionDetail['eventName'] = $actionDetail['pageTitle'];
}
@@ -322,8 +322,8 @@ class Visitor implements VisitorInterface
}
// Event value / Generation time
- if($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) {
- if(strlen($actionDetail['custom_float']) > 0) {
+ if ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) {
+ if (strlen($actionDetail['custom_float']) > 0) {
$actionDetail['eventValue'] = round($actionDetail['custom_float'], self::EVENT_VALUE_PRECISION);
}
} elseif ($actionDetail['custom_float'] > 0) {
diff --git a/plugins/Live/VisitorLog.php b/plugins/Live/VisitorLog.php
index 2afc67349b..054b2f50ae 100644
--- a/plugins/Live/VisitorLog.php
+++ b/plugins/Live/VisitorLog.php
@@ -105,7 +105,7 @@ class VisitorLog extends Visualization
&& $filterEcommerce == \Piwik\Plugins\Goals\Controller::ECOMMERCE_LOG_SHOW_ORDERS;
$isAbandonedCart = $action['type'] == 'ecommerceAbandonedCart'
&& $filterEcommerce == \Piwik\Plugins\Goals\Controller::ECOMMERCE_LOG_SHOW_ABANDONED_CARTS;
- if($isAbandonedCart || $isEcommerceOrder) {
+ if ($isAbandonedCart || $isEcommerceOrder) {
return true;
}
}
diff --git a/plugins/Login/Controller.php b/plugins/Login/Controller.php
index 82ae9ccab2..62c8b40740 100644
--- a/plugins/Login/Controller.php
+++ b/plugins/Login/Controller.php
@@ -9,6 +9,7 @@
namespace Piwik\Plugins\Login;
use Exception;
+use Piwik\Access;
use Piwik\Auth as AuthInterface;
use Piwik\Common;
use Piwik\Config;
@@ -278,7 +279,7 @@ class Controller extends \Piwik\Plugin\Controller
// have to do this as super user since redirectToIndex checks if there's a default website ID for
// the current user and if not, doesn't redirect to the requested action. TODO: this behavior is wrong. somehow.
$self = $this;
- Piwik::doAsSuperUser(function () use ($self) {
+ Access::doAsSuperUser(function () use ($self) {
$self->redirectToIndex(Piwik::getLoginPluginName(), 'resetPasswordSuccess');
});
return null;
@@ -330,4 +331,4 @@ class Controller extends \Piwik\Plugin\Controller
Url::redirectToUrl($logoutUrl);
}
}
-} \ No newline at end of file
+}
diff --git a/plugins/Login/Login.php b/plugins/Login/Login.php
index 4c33579176..8b65e2e175 100644
--- a/plugins/Login/Login.php
+++ b/plugins/Login/Login.php
@@ -85,7 +85,7 @@ class Login extends \Piwik\Plugin
*/
public static function initAuthenticationFromCookie(\Piwik\Auth $auth, $activateCookieAuth)
{
- if(self::isModuleIsAPI() && !$activateCookieAuth) {
+ if (self::isModuleIsAPI() && !$activateCookieAuth) {
return;
}
diff --git a/plugins/Login/PasswordResetter.php b/plugins/Login/PasswordResetter.php
index 479a8a10a3..d2631abdf9 100644
--- a/plugins/Login/PasswordResetter.php
+++ b/plugins/Login/PasswordResetter.php
@@ -8,6 +8,7 @@
namespace Piwik\Plugins\Login;
use Exception;
+use Piwik\Access;
use Piwik\Common;
use Piwik\Config;
use Piwik\IP;
@@ -209,7 +210,7 @@ class PasswordResetter
// reset password of user
$usersManager = $this->usersManagerApi;
- Piwik::doAsSuperUser(function () use ($usersManager, $user, $resetPassword) {
+ Access::doAsSuperUser(function () use ($usersManager, $user, $resetPassword) {
$usersManager->updateUser(
$user['login'], $resetPassword, $email = false, $alias = false, $isPasswordHashed = true);
});
@@ -359,7 +360,7 @@ class PasswordResetter
protected function getUserInformation($loginOrMail)
{
$usersManager = $this->usersManagerApi;
- return Piwik::doAsSuperUser(function () use ($loginOrMail, $usersManager) {
+ return Access::doAsSuperUser(function () use ($loginOrMail, $usersManager) {
$user = null;
if ($usersManager->userExists($loginOrMail)) {
$user = $usersManager->getUser($loginOrMail);
diff --git a/plugins/Login/SessionInitializer.php b/plugins/Login/SessionInitializer.php
index 1ede89c639..cd41b36295 100644
--- a/plugins/Login/SessionInitializer.php
+++ b/plugins/Login/SessionInitializer.php
@@ -14,6 +14,7 @@ use Piwik\AuthResult;
use Piwik\Config;
use Piwik\Cookie;
use Piwik\Db;
+use Piwik\Log;
use Piwik\Piwik;
use Piwik\Plugins\UsersManager\API as UsersManagerAPI;
use Piwik\ProxyHttp;
@@ -135,7 +136,17 @@ class SessionInitializer
*/
protected function doAuthenticateSession(AuthInterface $auth)
{
- $tokenAuth = $this->usersManagerAPI->getTokenAuth($auth->getLogin(), $auth->getTokenAuthSecret());
+ $login = $auth->getLogin();
+ $tokenAuthSecret = null;
+
+ try {
+ $tokenAuthSecret = $auth->getTokenAuthSecret();
+ } catch (Exception $ex) {
+ Log::debug("SessionInitializer::doAuthenticateSession: token_auth secret for %s not avaialble before user"
+ . " is authenticated.", $login);
+ }
+
+ $tokenAuth = empty($tokenAuthSecret) ? null : $this->usersManagerAPI->getTokenAuth($login, $tokenAuthSecret);
/**
* @deprecated Create a custom SessionInitializer instead.
diff --git a/plugins/MultiSites/API.php b/plugins/MultiSites/API.php
index 41031fae8f..ea393414f7 100755
--- a/plugins/MultiSites/API.php
+++ b/plugins/MultiSites/API.php
@@ -346,7 +346,7 @@ class API extends \Piwik\Plugin\API
{
$metrics = self::$baseMetrics;
- if(Common::isActionsPluginEnabled()) {
+ if (Common::isActionsPluginEnabled()) {
$metrics[self::NB_PAGEVIEWS_LABEL] = array(
self::METRIC_TRANSLATION_KEY => 'General_ColumnPageviews',
self::METRIC_EVOLUTION_COL_NAME_KEY => 'pageviews_evolution',
diff --git a/plugins/MultiSites/Menu.php b/plugins/MultiSites/Menu.php
index 5059cfc09d..8860eacec0 100644
--- a/plugins/MultiSites/Menu.php
+++ b/plugins/MultiSites/Menu.php
@@ -15,7 +15,7 @@ class Menu extends \Piwik\Plugin\Menu
{
public function configureTopMenu(MenuTop $menu)
{
- $urlParams = $this->urlForAction('index', array('segment' => false));
+ $urlParams = $this->urlForActionWithDefaultUserParams('index', array('segment' => false, 'idSite' => false));
$tooltip = Piwik::translate('MultiSites_TopLinkTooltip');
$menu->add('General_MultiSitesSummary', null, $urlParams, true, 3, $tooltip);
diff --git a/plugins/PrivacyManager/DoNotTrackHeaderChecker.php b/plugins/PrivacyManager/DoNotTrackHeaderChecker.php
index b857debf85..14938f426e 100644
--- a/plugins/PrivacyManager/DoNotTrackHeaderChecker.php
+++ b/plugins/PrivacyManager/DoNotTrackHeaderChecker.php
@@ -26,7 +26,7 @@ class DoNotTrackHeaderChecker
*/
public function checkHeaderInTracker(&$exclude)
{
- if($exclude) {
+ if ($exclude) {
Common::printDebug("Visit is already excluded, no need to check DoNotTrack support.");
return;
}
diff --git a/plugins/PrivacyManager/LogDataPurger.php b/plugins/PrivacyManager/LogDataPurger.php
index 2ec73deb81..6fe1381c50 100755
--- a/plugins/PrivacyManager/LogDataPurger.php
+++ b/plugins/PrivacyManager/LogDataPurger.php
@@ -173,7 +173,7 @@ class LogDataPurger
private function getLogTableDeleteCount($table, $maxIdVisit)
{
$sql = "SELECT COUNT(*) FROM $table WHERE idvisit <= ?";
- return (int)Db::fetchOne($sql, array($maxIdVisit));
+ return (int) Db::fetchOne($sql, array($maxIdVisit));
}
private function createTempTable()
diff --git a/plugins/PrivacyManager/ReportsPurger.php b/plugins/PrivacyManager/ReportsPurger.php
index a35169e6ef..9889c00e9d 100755
--- a/plugins/PrivacyManager/ReportsPurger.php
+++ b/plugins/PrivacyManager/ReportsPurger.php
@@ -260,12 +260,11 @@ class ReportsPurger
{
$maxIdArchive = Db::fetchOne("SELECT MAX(idarchive) FROM $table");
- $sql = "SELECT COUNT(*)
- FROM $table
- WHERE name NOT IN ('" . implode("','", $this->metricsToKeep) . "')
- AND name NOT LIKE 'done%'
- AND idarchive >= ?
- AND idarchive < ?";
+ $sql = "SELECT COUNT(*) FROM $table
+ WHERE name NOT IN ('" . implode("','", $this->metricsToKeep) . "')
+ AND name NOT LIKE 'done%'
+ AND idarchive >= ?
+ AND idarchive < ?";
$segments = Db::segmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize);
return array_sum($segments);
@@ -275,11 +274,10 @@ class ReportsPurger
{
$maxIdArchive = Db::fetchOne("SELECT MAX(idarchive) FROM $table");
- $sql = "SELECT COUNT(*)
- FROM $table
- WHERE " . $this->getBlobTableWhereExpr($oldNumericTables, $table) . "
- AND idarchive >= ?
- AND idarchive < ?";
+ $sql = "SELECT COUNT(*) FROM $table
+ WHERE " . $this->getBlobTableWhereExpr($oldNumericTables, $table) . "
+ AND idarchive >= ?
+ AND idarchive < ?";
$segments = Db::segmentedFetchOne($sql, 0, $maxIdArchive, self::$selectSegmentSize);
return array_sum($segments);
@@ -325,12 +323,11 @@ class ReportsPurger
$maxIdArchive = Db::fetchOne("SELECT MAX(idarchive) FROM $table");
- $sql = "SELECT idarchive
- FROM $table
- WHERE name != 'done'
- AND name LIKE 'done_%.%'
- AND idarchive >= ?
- AND idarchive < ?";
+ $sql = "SELECT idarchive FROM $table
+ WHERE name != 'done'
+ AND name LIKE 'done_%.%'
+ AND idarchive >= ?
+ AND idarchive < ?";
if (is_null($this->segmentArchiveIds)) {
$this->segmentArchiveIds = array();
diff --git a/plugins/Referrers/Columns/Base.php b/plugins/Referrers/Columns/Base.php
index de871401b3..a69fcc54bf 100644
--- a/plugins/Referrers/Columns/Base.php
+++ b/plugins/Referrers/Columns/Base.php
@@ -244,14 +244,14 @@ abstract class Base extends VisitDimension
protected function detectCampaignKeywordFromReferrerUrl()
{
- if(!empty($this->nameReferrerAnalyzed)
+ if (!empty($this->nameReferrerAnalyzed)
&& !empty($this->keywordReferrerAnalyzed)) {
// keyword is already set, we skip
return true;
}
// Set the Campaign keyword to the keyword found in the Referrer URL if any
- if(!empty($this->nameReferrerAnalyzed)) {
+ if (!empty($this->nameReferrerAnalyzed)) {
$referrerUrlInfo = UrlHelper::extractSearchEngineInformationFromUrl($this->referrerUrl);
if (!empty($referrerUrlInfo['keywords'])) {
$this->keywordReferrerAnalyzed = $referrerUrlInfo['keywords'];
@@ -270,7 +270,7 @@ abstract class Base extends VisitDimension
$parsedAdsenseReferrerUrl = parse_url($value);
if (!empty($parsedAdsenseReferrerUrl['host'])) {
- if(empty($this->nameReferrerAnalyzed)) {
+ if (empty($this->nameReferrerAnalyzed)) {
$type = $this->getParameterValueFromReferrerUrl('ad_type');
$type = $type ? " ($type)" : '';
$this->nameReferrerAnalyzed = self::LABEL_ADWORDS_NAME . $type;
@@ -304,7 +304,7 @@ abstract class Base extends VisitDimension
return false;
}
// if we detected a campaign but there is still no keyword set, we set the keyword to the Referrer host
- if(empty($this->keywordReferrerAnalyzed)) {
+ if (empty($this->keywordReferrerAnalyzed)) {
$this->keywordReferrerAnalyzed = $this->referrerHost;
}
diff --git a/plugins/Referrers/Controller.php b/plugins/Referrers/Controller.php
index e683cb7f9e..f01c61f647 100644
--- a/plugins/Referrers/Controller.php
+++ b/plugins/Referrers/Controller.php
@@ -332,7 +332,7 @@ function DisplayTopKeywords($url = "")
$url = empty($url) ? "http://". $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] : $url;
$api = "' . $api . '&url=" . urlencode($url);
$keywords = @unserialize(file_get_contents($api));
- if($keywords === false || isset($keywords["result"])) {
+ if ($keywords === false || isset($keywords["result"])) {
// DEBUG ONLY: uncomment for troubleshooting an empty output (the URL output reveals the token_auth)
// echo "Error while fetching the <a href=\'$api\'>Top Keywords from Piwik</a>";
return;
@@ -344,7 +344,7 @@ function DisplayTopKeywords($url = "")
foreach($keywords as $keyword) {
$output .= "<li>". $keyword . "</li>";
}
- if(empty($keywords)) { $output .= "Nothing yet..."; }
+ if (empty($keywords)) { $output .= "Nothing yet..."; }
$output .= "</ul>";
echo $output;
}
diff --git a/plugins/Referrers/functions.php b/plugins/Referrers/functions.php
index 8f7a4023df..e0fee30833 100644
--- a/plugins/Referrers/functions.php
+++ b/plugins/Referrers/functions.php
@@ -39,7 +39,7 @@ function getSocialMainUrl($url)
$social = getSocialNetworkFromDomain($url);
foreach (Common::getSocialUrls() as $domain => $name) {
- if($name == $social) {
+ if ($name == $social) {
return $domain;
}
@@ -57,7 +57,7 @@ function getSocialNetworkFromDomain($url)
{
foreach (Common::getSocialUrls() as $domain => $name) {
- if(preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url)) {
+ if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url)) {
return $name;
}
diff --git a/plugins/ScheduledReports/API.php b/plugins/ScheduledReports/API.php
index 3ea8a05c28..a42b5bb209 100644
--- a/plugins/ScheduledReports/API.php
+++ b/plugins/ScheduledReports/API.php
@@ -53,6 +53,9 @@ class API extends \Piwik\Plugin\API
const REPORT_TRUNCATE = 23;
+ // static cache storing reports
+ public static $cache = array();
+
/**
* Creates a new report and schedules it.
*
@@ -84,29 +87,20 @@ class API extends \Piwik\Plugin\API
// validation of requested reports
$reports = self::validateRequestedReports($idSite, $reportType, $reports);
- $db = Db::get();
- $idReport = $db->fetchOne("SELECT max(idreport) + 1 FROM " . Common::prefixTable('report'));
-
- if ($idReport == false) {
- $idReport = 1;
- }
-
- $db->insert(Common::prefixTable('report'),
- array(
- 'idreport' => $idReport,
- 'idsite' => $idSite,
- 'login' => $currentUser,
- 'description' => $description,
- 'idsegment' => $idSegment,
- 'period' => $period,
- 'hour' => $hour,
- 'type' => $reportType,
- 'format' => $reportFormat,
- 'parameters' => $parameters,
- 'reports' => $reports,
- 'ts_created' => Date::now()->getDatetime(),
- 'deleted' => 0,
- ));
+ $idReport = $this->getModel()->createReport(array(
+ 'idsite' => $idSite,
+ 'login' => $currentUser,
+ 'description' => $description,
+ 'idsegment' => $idSegment,
+ 'period' => $period,
+ 'hour' => $hour,
+ 'type' => $reportType,
+ 'format' => $reportFormat,
+ 'parameters' => $parameters,
+ 'reports' => $reports,
+ 'ts_created' => Date::now()->getDatetime(),
+ 'deleted' => 0,
+ ));
return $idReport;
}
@@ -130,7 +124,7 @@ class API extends \Piwik\Plugin\API
Piwik::checkUserHasViewAccess($idSite);
$scheduledReports = $this->getReports($idSite, $periodSearch = false, $idReport);
- $report = reset($scheduledReports);
+ $report = reset($scheduledReports);
$idReport = $report['idreport'];
$currentUser = Piwik::getCurrentUserLogin();
@@ -144,19 +138,16 @@ class API extends \Piwik\Plugin\API
// validation of requested reports
$reports = self::validateRequestedReports($idSite, $reportType, $reports);
- Db::get()->update(Common::prefixTable('report'),
- array(
- 'description' => $description,
- 'idsegment' => $idSegment,
- 'period' => $period,
- 'hour' => $hour,
- 'type' => $reportType,
- 'format' => $reportFormat,
- 'parameters' => $parameters,
- 'reports' => $reports,
- ),
- "idreport = '$idReport'"
- );
+ $this->getModel()->updateReport($idReport, array(
+ 'description' => $description,
+ 'idsegment' => $idSegment,
+ 'period' => $period,
+ 'hour' => $hour,
+ 'type' => $reportType,
+ 'format' => $reportFormat,
+ 'parameters' => $parameters,
+ 'reports' => $reports,
+ ));
self::$cache = array();
}
@@ -172,18 +163,13 @@ class API extends \Piwik\Plugin\API
$report = reset($APIScheduledReports);
Piwik::checkUserHasSuperUserAccessOrIsTheUser($report['login']);
- Db::get()->update(Common::prefixTable('report'),
- array(
- 'deleted' => 1,
- ),
- "idreport = '$idReport'"
- );
+ $this->getModel()->updateReport($idReport, array(
+ 'deleted' => 1,
+ ));
+
self::$cache = array();
}
- // static cache storing reports
- public static $cache = array();
-
/**
* Returns the list of reports matching the passed parameters
*
@@ -198,6 +184,7 @@ class API extends \Piwik\Plugin\API
public function getReports($idSite = false, $period = false, $idReport = false, $ifSuperUserReturnOnlySuperUserReports = false, $idSegment = false)
{
Piwik::checkUserHasSomeViewAccess();
+
$cacheKey = (int)$idSite . '.' . (string)$period . '.' . (int)$idReport . '.' . (int)$ifSuperUserReturnOnlySuperUserReports;
if (isset(self::$cache[$cacheKey])) {
return self::$cache[$cacheKey];
@@ -427,7 +414,7 @@ class API extends \Piwik\Plugin\API
array(&$reportRenderer, $reportType, $outputType, $report)
);
- if(is_null($reportRenderer)) {
+ if (is_null($reportRenderer)) {
throw new Exception("A report renderer was not supplied in the event " . self::GET_RENDERER_INSTANCE_EVENT);
}
@@ -556,10 +543,8 @@ class API extends \Piwik\Plugin\API
);
// Update flag in DB
- Db::get()->update(Common::prefixTable('report'),
- array('ts_last_sent' => Date::now()->getDatetime()),
- "idreport = " . $report['idreport']
- );
+ $now = Date::now()->getDatetime();
+ $this->getModel()->updateReport($report['idreport'], array('ts_last_sent' => $now));
// If running from piwik.php with debug, do not delete the PDF after sending the email
if (!isset($GLOBALS['PIWIK_TRACKER_DEBUG']) || !$GLOBALS['PIWIK_TRACKER_DEBUG']) {
@@ -567,6 +552,11 @@ class API extends \Piwik\Plugin\API
}
}
+ private function getModel()
+ {
+ return new Model();
+ }
+
private static function getReportSubjectAndReportTitle($websiteName, $reports)
{
// if the only report is "All websites", we don't display the site name
diff --git a/plugins/ScheduledReports/Model.php b/plugins/ScheduledReports/Model.php
new file mode 100644
index 0000000000..01507f65f9
--- /dev/null
+++ b/plugins/ScheduledReports/Model.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\ScheduledReports;
+
+use Piwik\Common;
+use Piwik\Db;
+use Piwik\DbHelper;
+use Piwik\ReportRenderer;
+use Piwik\Translate;
+
+class Model
+{
+ private static $rawPrefix = 'report';
+ private $table;
+
+ public function __construct()
+ {
+ $this->table = Common::prefixTable(self::$rawPrefix);
+ }
+
+ public function deleteUserReportForSite($userLogin, $idSite)
+ {
+ $query = 'DELETE FROM ' . $this->table . ' WHERE login = ? and idsite = ?';
+ $bind = array($userLogin, $idSite);
+ Db::query($query, $bind);
+ }
+
+ public function deleteAllReportForUser($userLogin)
+ {
+ Db::query('DELETE FROM ' . $this->table . ' WHERE login = ?', $userLogin);
+ }
+
+ public function updateReport($idReport, $report)
+ {
+ $idReport = (int) $idReport;
+
+ $this->getDb()->update($this->table, $report, "idreport = " . $idReport);
+ }
+
+ public function createReport($report)
+ {
+ $nextId = $this->getNextReportId();
+ $report['idreport'] = $nextId;
+
+ $this->getDb()->insert($this->table, $report);
+
+ return $nextId;
+ }
+
+ private function getNextReportId()
+ {
+ $db = $this->getDb();
+ $idReport = $db->fetchOne("SELECT max(idreport) + 1 FROM " . $this->table);
+
+ if ($idReport == false) {
+ $idReport = 1;
+ }
+
+ return $idReport;
+ }
+
+ private function getDb()
+ {
+ return Db::get();
+ }
+
+ public static function install()
+ {
+ $reportTable = "`idreport` INT(11) NOT NULL AUTO_INCREMENT,
+ `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,
+ `format` VARCHAR(10) NOT NULL,
+ `reports` TEXT NOT NULL,
+ `parameters` TEXT NULL,
+ `ts_created` TIMESTAMP NULL,
+ `ts_last_sent` TIMESTAMP NULL,
+ `deleted` tinyint(4) NOT NULL default 0,
+ PRIMARY KEY (`idreport`)";
+
+ DbHelper::createTable(self::$rawPrefix, $reportTable);
+ }
+}
diff --git a/plugins/ScheduledReports/ScheduledReports.php b/plugins/ScheduledReports/ScheduledReports.php
index ca5ebb9b41..e59e9140ab 100644
--- a/plugins/ScheduledReports/ScheduledReports.php
+++ b/plugins/ScheduledReports/ScheduledReports.php
@@ -9,9 +9,7 @@
namespace Piwik\Plugins\ScheduledReports;
use Exception;
-use Piwik\Common;
use Piwik\Db;
-use Piwik\DbHelper;
use Piwik\Mail;
use Piwik\Piwik;
use Piwik\Plugins\MobileMessaging\MobileMessaging;
@@ -469,27 +467,27 @@ class ScheduledReports extends \Piwik\Plugin
$reportsNeedSegment = array();
- if(!$updatedSegment['enable_all_users']) {
+ if (!$updatedSegment['enable_all_users']) {
// which reports would become invisible to other users?
foreach($reportsUsingSegment as $report) {
- if($report['login'] == Piwik::getCurrentUserLogin()) {
+ if ($report['login'] == Piwik::getCurrentUserLogin()) {
continue;
}
$reportsNeedSegment[] = $report;
}
}
- if($updatedSegment['enable_only_idsite']) {
+ if ($updatedSegment['enable_only_idsite']) {
// which reports from other websites are set to use this segment restricted to one website?
foreach($reportsUsingSegment as $report) {
- if($report['idsite'] == $updatedSegment['enable_only_idsite']) {
+ if ($report['idsite'] == $updatedSegment['enable_only_idsite']) {
continue;
}
$reportsNeedSegment[] = $report;
}
}
- if(empty($reportsNeedSegment)) {
+ if (empty($reportsNeedSegment)) {
return;
}
@@ -526,7 +524,7 @@ class ScheduledReports extends \Piwik\Plugin
public function deleteUserReport($userLogin)
{
- Db::query('DELETE FROM ' . Common::prefixTable('report') . ' WHERE login = ?', $userLogin);
+ $this->getModel()->deleteAllReportForUser($userLogin);
}
public function deleteUserReportForSites($userLogin, $idSites)
@@ -535,33 +533,21 @@ class ScheduledReports extends \Piwik\Plugin
return;
}
- $table = Common::prefixTable('report');
+ $model = $this->getModel();
foreach ($idSites as $idSite) {
- Db::query('DELETE FROM ' . $table . ' WHERE login = ? and idsite = ?',
- array($userLogin, $idSite));
+ $model->deleteUserReportForSite($userLogin, $idSite);
}
}
+ private function getModel()
+ {
+ return new Model();
+ }
+
public function install()
{
- $reportTable = "`idreport` INT(11) NOT NULL AUTO_INCREMENT,
- `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,
- `format` VARCHAR(10) NOT NULL,
- `reports` TEXT NOT NULL,
- `parameters` TEXT NULL,
- `ts_created` TIMESTAMP NULL,
- `ts_last_sent` TIMESTAMP NULL,
- `deleted` tinyint(4) NOT NULL default 0,
- PRIMARY KEY (`idreport`)";
-
- DbHelper::createTable('report', $reportTable);
+ Model::install();
}
private static function checkAdditionalEmails($additionalEmails)
diff --git a/plugins/SegmentEditor/API.php b/plugins/SegmentEditor/API.php
index 98b752cc2a..699ad3a4ea 100644
--- a/plugins/SegmentEditor/API.php
+++ b/plugins/SegmentEditor/API.php
@@ -103,6 +103,7 @@ class API extends \Piwik\Plugin\API
if (empty($segment)) {
throw new Exception("Requested segment not found");
}
+
return $segment;
}
@@ -128,7 +129,7 @@ class API extends \Piwik\Plugin\API
public function isUserCanAddNewSegment($idSite)
{
- if(Piwik::isUserIsAnonymous()) {
+ if (Piwik::isUserIsAnonymous()) {
return false;
}
@@ -145,13 +146,13 @@ class API extends \Piwik\Plugin\API
protected function checkUserCanEditOrDeleteSegment($segment)
{
- if(Piwik::hasUserSuperUserAccess()) {
+ if (Piwik::hasUserSuperUserAccess()) {
return;
}
$this->checkUserIsNotAnonymous();
- if($segment['login'] != Piwik::getCurrentUserLogin()) {
+ if ($segment['login'] != Piwik::getCurrentUserLogin()) {
throw new Exception($this->getMessageCannotEditSegmentCreatedBySuperUser());
}
}
@@ -177,11 +178,16 @@ class API extends \Piwik\Plugin\API
*/
Piwik::postEvent('SegmentEditor.deactivate', array($idSegment));
- $db = Db::get();
- $db->delete(Common::prefixTable('segment'), 'idsegment = ' . $idSegment);
+ $this->getModel()->deleteSegment($idSegment);
+
return true;
}
+ private function getModel()
+ {
+ return new Model();
+ }
+
/**
* Modifies an existing stored segment.
*
@@ -201,9 +207,9 @@ class API extends \Piwik\Plugin\API
$idSite = $this->checkIdSite($idSite);
$this->checkSegmentName($name);
- $definition = $this->checkSegmentValue($definition, $idSite);
+ $definition = $this->checkSegmentValue($definition, $idSite);
$enabledAllUsers = $this->checkEnabledAllUsers($enabledAllUsers);
- $autoArchive = $this->checkAutoArchive($autoArchive, $idSite);
+ $autoArchive = $this->checkAutoArchive($autoArchive, $idSite);
$bind = array(
'name' => $name,
@@ -224,11 +230,8 @@ class API extends \Piwik\Plugin\API
*/
Piwik::postEvent('SegmentEditor.update', array($idSegment, $bind));
- $db = Db::get();
- $db->update(Common::prefixTable("segment"),
- $bind,
- "idsegment = $idSegment"
- );
+ $this->getModel()->updateSegment($idSegment, $bind);
+
return true;
}
@@ -252,7 +255,6 @@ class API extends \Piwik\Plugin\API
$enabledAllUsers = $this->checkEnabledAllUsers($enabledAllUsers);
$autoArchive = $this->checkAutoArchive($autoArchive, $idSite);
- $db = Db::get();
$bind = array(
'name' => $name,
'definition' => $definition,
@@ -263,8 +265,10 @@ class API extends \Piwik\Plugin\API
'ts_created' => Date::now()->getDatetime(),
'deleted' => 0,
);
- $db->insert(Common::prefixTable("segment"), $bind);
- return $db->lastInsertId();
+
+ $id = $this->getModel()->createSegment($bind);
+
+ return $id;
}
/**
@@ -277,12 +281,12 @@ class API extends \Piwik\Plugin\API
public function get($idSegment)
{
Piwik::checkUserHasSomeViewAccess();
+
if (!is_numeric($idSegment)) {
throw new Exception("idSegment should be numeric.");
}
- $segment = Db::get()->fetchRow("SELECT * " .
- " FROM " . Common::prefixTable("segment") .
- " WHERE idsegment = ?", $idSegment);
+
+ $segment = $this->getModel()->getSegment($idSegment);
if (empty($segment)) {
return false;
@@ -300,6 +304,7 @@ class API extends \Piwik\Plugin\API
if ($segment['deleted']) {
throw new Exception("This segment is marked as deleted. ");
}
+
return $segment;
}
@@ -319,7 +324,7 @@ class API extends \Piwik\Plugin\API
$userLogin = Piwik::getCurrentUserLogin();
- $model = new Model();
+ $model = $this->getModel();
if (empty($idSite)) {
$segments = $model->getAllSegments($userLogin);
} else {
diff --git a/plugins/SegmentEditor/Model.php b/plugins/SegmentEditor/Model.php
index 54c0e07ddf..8bfcc957ae 100644
--- a/plugins/SegmentEditor/Model.php
+++ b/plugins/SegmentEditor/Model.php
@@ -10,12 +10,21 @@ namespace Piwik\Plugins\SegmentEditor;
use Piwik\Common;
use Piwik\Db;
+use Piwik\DbHelper;
/**
* The SegmentEditor Model lets you persist and read custom Segments from the backend without handling any logic.
*/
class Model
{
+ private static $rawPrefix = 'segment';
+ private $table;
+
+ public function __construct()
+ {
+ $this->table = Common::prefixTable(self::$rawPrefix);
+ }
+
/**
* Returns all stored segments.
*
@@ -36,7 +45,7 @@ class Model
$sql = $this->buildQuerySortedByName("($whereIdSite enable_only_idsite = 0)
AND deleted = 0 AND auto_archive = 1");
- $segments = Db::get()->fetchAll($sql, $bind);
+ $segments = $this->getDb()->fetchAll($sql, $bind);
return $segments;
}
@@ -52,7 +61,7 @@ class Model
$bind = array($userLogin);
$sql = $this->buildQuerySortedByName('deleted = 0 AND (enable_all_users = 1 OR login = ?)');
- $segments = Db::get()->fetchAll($sql, $bind);
+ $segments = $this->getDb()->fetchAll($sql, $bind);
return $segments;
}
@@ -70,16 +79,69 @@ class Model
$sql = $this->buildQuerySortedByName('(enable_only_idsite = ? OR enable_only_idsite = 0)
AND deleted = 0
AND (enable_all_users = 1 OR login = ?)');
- $segments = Db::get()->fetchAll($sql, $bind);
+ $segments = $this->getDb()->fetchAll($sql, $bind);
return $segments;
}
+ public function deleteSegment($idSegment)
+ {
+ $db = $this->getDb();
+ $db->delete($this->table, 'idsegment = ' . (int) $idSegment);
+ }
+
+ public function updateSegment($idSegment, $segment)
+ {
+ $idSegment = (int) $idSegment;
+
+ $db = $this->getDb();
+ $db->update($this->table, $segment, "idsegment = $idSegment");
+
+ return true;
+ }
+
+ public function createSegment($segment)
+ {
+ $db = $this->getDb();
+ $db->insert($this->table, $segment);
+ $id = $db->lastInsertId();
+
+ return $id;
+ }
+
+ public function getSegment($idSegment)
+ {
+ $db = $this->getDb();
+ $segment = $db->fetchRow("SELECT * FROM " . $this->table . " WHERE idsegment = ?", $idSegment);
+
+ return $segment;
+ }
+
+ private function getDb()
+ {
+ return Db::get();
+ }
+
private function buildQuerySortedByName($where)
{
- $sql = "SELECT * FROM " . Common::prefixTable("segment") .
- " WHERE $where ORDER BY name ASC";
+ return "SELECT * FROM " . $this->table . " WHERE $where ORDER BY name ASC";
+ }
+
+ public static function install()
+ {
+ $segmentTable = "`idsegment` INT(11) NOT NULL AUTO_INCREMENT,
+ `name` VARCHAR(255) NOT NULL,
+ `definition` TEXT NOT NULL,
+ `login` VARCHAR(100) NOT NULL,
+ `enable_all_users` tinyint(4) NOT NULL default 0,
+ `enable_only_idsite` INTEGER(11) NULL,
+ `auto_archive` tinyint(4) NOT NULL default 0,
+ `ts_created` TIMESTAMP NULL,
+ `ts_last_edit` TIMESTAMP NULL,
+ `deleted` tinyint(4) NOT NULL default 0,
+ PRIMARY KEY (`idsegment`)";
- return $sql;
+ DbHelper::createTable(self::$rawPrefix, $segmentTable);
}
+
}
diff --git a/plugins/SegmentEditor/SegmentEditor.php b/plugins/SegmentEditor/SegmentEditor.php
index bd957c54c0..aef619156d 100644
--- a/plugins/SegmentEditor/SegmentEditor.php
+++ b/plugins/SegmentEditor/SegmentEditor.php
@@ -67,27 +67,17 @@ class SegmentEditor extends \Piwik\Plugin
{
$model = new Model();
$segmentToAutoArchive = $model->getSegmentsToAutoArchive($idSite);
+
foreach ($segmentToAutoArchive as $segmentInfo) {
$segments[] = $segmentInfo['definition'];
}
+
$segments = array_unique($segments);
}
public function install()
{
- $segmentTable = "`idsegment` INT(11) NOT NULL AUTO_INCREMENT,
- `name` VARCHAR(255) NOT NULL,
- `definition` TEXT NOT NULL,
- `login` VARCHAR(100) NOT NULL,
- `enable_all_users` tinyint(4) NOT NULL default 0,
- `enable_only_idsite` INTEGER(11) NULL,
- `auto_archive` tinyint(4) NOT NULL default 0,
- `ts_created` TIMESTAMP NULL,
- `ts_last_edit` TIMESTAMP NULL,
- `deleted` tinyint(4) NOT NULL default 0,
- PRIMARY KEY (`idsegment`)";
-
- DbHelper::createTable('segment', $segmentTable);
+ Model::install();
}
public function getJsFiles(&$jsFiles)
diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php
index 5b856cd862..1bb4d4b304 100644
--- a/plugins/SitesManager/API.php
+++ b/plugins/SitesManager/API.php
@@ -143,11 +143,9 @@ class API extends \Piwik\Plugin\API
public function getSitesFromGroup($group)
{
Piwik::checkUserHasSuperUserAccess();
- $group = trim($group);
- $sites = Db::get()->fetchAll("SELECT *
- FROM " . Common::prefixTable("site") . "
- WHERE `group` = ?", $group);
+ $group = trim($group);
+ $sites = $this->getModel()->getSitesFromGroup($group);
Site::setSitesFromArray($sites);
return $sites;
@@ -162,12 +160,10 @@ class API extends \Piwik\Plugin\API
public function getSitesGroups()
{
Piwik::checkUserHasSuperUserAccess();
- $groups = Db::get()->fetchAll("SELECT DISTINCT `group` FROM " . Common::prefixTable("site"));
- $cleanedGroups = array();
- foreach ($groups as $group) {
- $cleanedGroups[] = $group['group'];
- }
- $cleanedGroups = array_map('trim', $cleanedGroups);
+
+ $groups = $this->getModel()->getSitesGroups();
+ $cleanedGroups = array_map('trim', $groups);
+
return $cleanedGroups;
}
@@ -219,12 +215,15 @@ class API extends \Piwik\Plugin\API
public function getAllSites()
{
Piwik::checkUserHasSuperUserAccess();
- $sites = Db::get()->fetchAll("SELECT * FROM " . Common::prefixTable("site") . " ORDER BY idsite ASC");
+
+ $sites = $this->getModel()->getAllSites();
$return = array();
foreach ($sites as $site) {
$return[$site['idsite']] = $site;
}
+
Site::setSitesFromArray($return);
+
return $return;
}
@@ -258,24 +257,16 @@ class API extends \Piwik\Plugin\API
if (empty($timestamp)) $timestamp = time();
- $time = Date::factory((int)$timestamp)->getDatetime();
- $result = Db::fetchAll("
- SELECT
- idsite
- FROM
- " . Common::prefixTable('site') . " s
- WHERE EXISTS (
- SELECT 1
- FROM " . Common::prefixTable('log_visit') . " v
- WHERE v.idsite = s.idsite
- AND visit_last_action_time > ?
- AND visit_last_action_time <= ?
- LIMIT 1)
- ", array($time, $now = Date::now()->addHour(1)->getDatetime()));
+ $time = Date::factory((int)$timestamp)->getDatetime();
+ $now = Date::now()->addHour(1)->getDatetime();
+
+ $result = $this->getModel()->getSitesWithVisits($time, $now);
+
$idSites = array();
foreach ($result as $idSite) {
$idSites[] = $idSite['idsite'];
}
+
return $idSites;
}
@@ -291,9 +282,11 @@ class API extends \Piwik\Plugin\API
$sitesId = $this->getSitesIdWithAdminAccess();
$sites = $this->getSitesFromIds($sitesId);
- if ($fetchAliasUrls)
- foreach ($sites as &$site)
+ if ($fetchAliasUrls) {
+ foreach ($sites as &$site) {
$site['alias_urls'] = API::getInstance()->getSiteUrlsFromId($site['idsite']);
+ }
+ }
return $sites;
}
@@ -373,10 +366,12 @@ class API extends \Piwik\Plugin\API
}
$accessRaw = Access::getInstance()->getRawSitesWithSomeViewAccess($_restrictSitesToLogin);
- $sitesId = array();
+ $sitesId = array();
+
foreach ($accessRaw as $access) {
$sitesId[] = $access['idsite'];
}
+
return $sitesId;
} else {
return Access::getInstance()->getSitesIdWithAtLeastViewAccess();
@@ -407,6 +402,7 @@ class API extends \Piwik\Plugin\API
} else {
$urlBis = str_replace('://', '://www.', $url);
}
+
return array($url, $urlBis);
}
@@ -420,28 +416,12 @@ class API extends \Piwik\Plugin\API
{
$url = $this->removeTrailingSlash($url);
list($url, $urlBis) = $this->getNormalizedUrls($url);
+
if (Piwik::hasUserSuperUserAccess()) {
- $ids = Db::get()->fetchAll(
- 'SELECT idsite
- FROM ' . Common::prefixTable('site') . '
- WHERE (main_url = ? OR main_url = ?) ' .
- 'UNION
- SELECT idsite
- FROM ' . Common::prefixTable('site_url') . '
- WHERE (url = ? OR url = ?) ', array($url, $urlBis, $url, $urlBis));
+ $ids = $this->getModel()->getAllSitesIdFromSiteUrl($url, $urlBis);
} else {
$login = Piwik::getCurrentUserLogin();
- $ids = Db::get()->fetchAll(
- 'SELECT idsite
- FROM ' . Common::prefixTable('site') . '
- WHERE (main_url = ? OR main_url = ?)' .
- 'AND idsite IN (' . Access::getSqlAccessSite('idsite') . ') ' .
- 'UNION
- SELECT idsite
- FROM ' . Common::prefixTable('site_url') . '
- WHERE (url = ? OR url = ?)' .
- 'AND idsite IN (' . Access::getSqlAccessSite('idsite') . ')',
- array($url, $urlBis, $login, $url, $urlBis, $login));
+ $ids = $this->getModel()->getSitesIdFromSiteUrlHavingAccess($url, $urlBis, $login);
}
return $ids;
@@ -457,18 +437,17 @@ class API extends \Piwik\Plugin\API
public function getSitesIdFromTimezones($timezones)
{
Piwik::checkUserHasSuperUserAccess();
+
$timezones = Piwik::getArrayFromApiParameter($timezones);
$timezones = array_unique($timezones);
- $ids = Db::get()->fetchAll(
- 'SELECT idsite
- FROM ' . Common::prefixTable('site') . '
- WHERE timezone IN (' . Common::getSqlStringFieldsArray($timezones) . ')
- ORDER BY idsite ASC',
- $timezones);
+
+ $ids = $this->getModel()->getSitesFromTimezones($timezones);
+
$return = array();
foreach ($ids as $id) {
$return[] = $id['idsite'];
}
+
return $return;
}
@@ -538,9 +517,7 @@ class API extends \Piwik\Plugin\API
}
$this->checkValidCurrency($currency);
- $db = Db::get();
-
- $url = $urls[0];
+ $url = $urls[0];
$urls = array_slice($urls, 1);
$bind = array('name' => $siteName,
@@ -549,31 +526,31 @@ class API extends \Piwik\Plugin\API
);
$bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps);
- $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters);
+ $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters);
$bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents);
- $bind['keep_url_fragment'] = $keepURLFragments;
- $bind['timezone'] = $timezone;
- $bind['currency'] = $currency;
- $bind['ecommerce'] = (int)$ecommerce;
+ $bind['keep_url_fragment'] = $keepURLFragments;
+ $bind['timezone'] = $timezone;
+ $bind['currency'] = $currency;
+ $bind['ecommerce'] = (int)$ecommerce;
$bind['sitesearch'] = $siteSearch;
- $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters;
+ $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters;
$bind['sitesearch_category_parameters'] = $searchCategoryParameters;
- $bind['ts_created'] = !is_null($startDate)
- ? Date::factory($startDate)->getDatetime()
- : Date::now()->getDatetime();
+
+ if (is_null($startDate)) {
+ $bind['ts_created'] = Date::now()->getDatetime();
+ } else {
+ $bind['ts_created'] = Date::factory($startDate)->getDatetime();
+ }
+
$bind['type'] = $this->checkAndReturnType($type);
- if (!empty($group)
- && Piwik::hasUserSuperUserAccess()
- ) {
+ if (!empty($group) && Piwik::hasUserSuperUserAccess()) {
$bind['group'] = trim($group);
} else {
$bind['group'] = "";
}
- $db->insert(Common::prefixTable("site"), $bind);
-
- $idSite = $db->lastInsertId();
+ $idSite = $this->getModel()->createSite($bind);
$this->insertSiteUrls($idSite, $urls);
@@ -588,7 +565,7 @@ class API extends \Piwik\Plugin\API
*/
Piwik::postEvent('SitesManager.addSite.end', array($idSite));
- return (int)$idSite;
+ return (int) $idSite;
}
private function postUpdateWebsite($idSite)
@@ -619,16 +596,7 @@ class API extends \Piwik\Plugin\API
throw new Exception(Piwik::translate("SitesManager_ExceptionDeleteSite"));
}
- $db = Db::get();
-
- $db->query("DELETE FROM " . Common::prefixTable("site") . "
- WHERE idsite = ?", $idSite);
-
- $db->query("DELETE FROM " . Common::prefixTable("site_url") . "
- WHERE idsite = ?", $idSite);
-
- $db->query("DELETE FROM " . Common::prefixTable("access") . "
- WHERE idsite = ?", $idSite);
+ $this->getModel()->deleteSite($idSite);
// we do not delete logs here on purpose (you can run these queries on the log_ tables to delete all data)
Cache::deleteCacheWebsiteAttributes($idSite);
@@ -682,12 +650,14 @@ class API extends \Piwik\Plugin\API
private function checkAndReturnType($type)
{
- if(empty($type)) {
+ if (empty($type)) {
$type = Site::DEFAULT_SITE_TYPE;
}
- if(!is_string($type)) {
+
+ if (!is_string($type)) {
throw new Exception("Invalid website type $type");
}
+
return $type;
}
@@ -704,14 +674,17 @@ class API extends \Piwik\Plugin\API
if (empty($excludedIps)) {
return '';
}
+
$ips = explode(',', $excludedIps);
$ips = array_map('trim', $ips);
$ips = array_filter($ips, 'strlen');
+
foreach ($ips as $ip) {
if (!$this->isValidIp($ip)) {
throw new Exception(Piwik::translate('SitesManager_ExceptionInvalidIPFormat', array($ip, "1.2.3.4, 1.2.3.*, or 1.2.3.4/5")));
}
}
+
$ips = implode(',', $ips);
return $ips;
}
@@ -756,7 +729,7 @@ class API extends \Piwik\Plugin\API
$urls = $this->cleanParameterUrls($urls);
$this->checkUrls($urls);
- $this->deleteSiteAliasUrls($idSite);
+ $this->getModel()->deleteSiteAliasUrls($idSite);
$this->insertSiteUrls($idSite, $urls);
$this->postUpdateWebsite($idSite);
@@ -1131,17 +1104,15 @@ class API extends \Piwik\Plugin\API
$bind['sitesearch_category_parameters'] = $searchCategoryParameters;
$bind['type'] = $this->checkAndReturnType($type);
- $db = Db::get();
- $db->update(Common::prefixTable("site"),
- $bind,
- "idsite = $idSite"
- );
+ $this->getModel()->updateSite($bind, $idSite);
// we now update the main + alias URLs
- $this->deleteSiteAliasUrls($idSite);
+ $this->getModel()->deleteSiteAliasUrls($idSite);
+
if (count($urls) > 1) {
$this->addSiteAliasUrls($idSite, array_slice($urls, 1));
}
+
$this->postUpdateWebsite($idSite);
}
@@ -1158,14 +1129,9 @@ class API extends \Piwik\Plugin\API
$idSites = Site::getIdSitesFromIdSitesString($idSites);
Piwik::checkUserHasAdminAccess($idSites);
- // Update piwik_site.ts_created
- $query = "UPDATE " . Common::prefixTable("site") .
- " SET ts_created = ?" .
- " WHERE idsite IN ( " . implode(",", $idSites) . " )
- AND ts_created > ?";
$minDateSql = $minDate->subDay(1)->getDatetime();
- $bind = array($minDateSql, $minDateSql);
- Db::query($query, $bind);
+
+ $this->getModel()->updateSiteCreatedTime($idSites, $minDateSql);
}
private function checkAndReturnCommaSeparatedStringList($parameters)
@@ -1296,12 +1262,8 @@ class API extends \Piwik\Plugin\API
public function getUniqueSiteTimezones()
{
Piwik::checkUserHasSuperUserAccess();
- $results = Db::fetchAll("SELECT distinct timezone FROM " . Common::prefixTable('site'));
- $timezones = array();
- foreach ($results as $result) {
- $timezones[] = $result['timezone'];
- }
- return $timezones;
+
+ return $this->getModel()->getUniqueSiteTimezones();
}
/**
@@ -1311,14 +1273,9 @@ class API extends \Piwik\Plugin\API
private function insertSiteUrls($idSite, $urls)
{
if (count($urls) != 0) {
- $db = Db::get();
foreach ($urls as $url) {
try {
- $db->insert(Common::prefixTable("site_url"), array(
- 'idsite' => $idSite,
- 'url' => $url
- )
- );
+ $this->getModel()->insertSiteUrl($idSite, $url);
} catch(Exception $e) {
// See bug #4149
}
@@ -1327,16 +1284,6 @@ class API extends \Piwik\Plugin\API
}
/**
- * Delete all the alias URLs for the given idSite.
- */
- private function deleteSiteAliasUrls($idsite)
- {
- $db = Db::get();
- $db->query("DELETE FROM " . Common::prefixTable("site_url") . "
- WHERE idsite = ?", $idsite);
- }
-
- /**
* Remove the final slash in the URLs if found
*
* @param string $url
@@ -1350,6 +1297,7 @@ class API extends \Piwik\Plugin\API
) {
$url = substr($url, 0, strlen($url) - 1);
}
+
return $url;
}
@@ -1404,6 +1352,7 @@ class API extends \Piwik\Plugin\API
if (empty($searchKeywordParameters)) {
$searchKeywordParameters = '';
}
+
if (empty($searchCategoryParameters)) {
$searchCategoryParameters = '';
}
@@ -1439,9 +1388,10 @@ class API extends \Piwik\Plugin\API
if (!is_array($urls)) {
$urls = array($urls);
}
- $urls = array_filter($urls);
+ $urls = array_filter($urls);
$urls = array_map('urldecode', $urls);
+
foreach ($urls as &$url) {
$url = $this->removeTrailingSlash($url);
if (strpos($url, 'http') !== 0) {
@@ -1450,6 +1400,7 @@ class API extends \Piwik\Plugin\API
$url = trim($url);
$url = Common::sanitizeInputValue($url);
}
+
$urls = array_unique($urls);
return $urls;
}
@@ -1489,30 +1440,9 @@ class API extends \Piwik\Plugin\API
return array();
}
- $ids_str = '';
- foreach ($ids as $id_val) {
- $ids_str .= $id_val . ' , ';
- }
- $ids_str .= $id_val;
-
- $db = Db::get();
- $bind = array('%' . $pattern . '%', 'http%' . $pattern . '%', '%' . $pattern . '%');
+ $limit = SettingsPiwik::getWebsitesCountToDisplay();
+ $sites = $this->getModel()->getPatternMatchSites($ids, $pattern, $limit);
- // Also match the idsite
- $where = '';
- if (is_numeric($pattern)) {
- $bind[] = $pattern;
- $where = 'OR s.idsite = ?';
- }
- $sites = $db->fetchAll("SELECT idsite, name, main_url, `group`
- FROM " . Common::prefixTable('site') . " s
- WHERE ( s.name like ?
- OR s.main_url like ?
- OR s.`group` like ?
- $where )
- AND idsite in ($ids_str)
- LIMIT " . SettingsPiwik::getWebsitesCountToDisplay(),
- $bind);
return $sites;
}
diff --git a/plugins/SitesManager/Model.php b/plugins/SitesManager/Model.php
index 207f59bdfd..971844c40b 100644
--- a/plugins/SitesManager/Model.php
+++ b/plugins/SitesManager/Model.php
@@ -8,13 +8,172 @@
*/
namespace Piwik\Plugins\SitesManager;
+use Piwik\Access;
+use Piwik\Date;
use Piwik\Db;
use Piwik\Common;
use Exception;
-use Piwik\Site;
class Model
{
+ private static $rawPrefix = 'site';
+ private $table;
+
+ public function __construct()
+ {
+ $this->table = Common::prefixTable(self::$rawPrefix);
+ }
+
+ public function createSite($site)
+ {
+ $db = $this->getDb();
+ $db->insert($this->table, $site);
+
+ $idSite = $db->lastInsertId();
+
+ return $idSite;
+ }
+
+ /**
+ * Returns all websites belonging to the specified group
+ * @param string $group Group name
+ * @return array of sites
+ */
+ public function getSitesFromGroup($group)
+ {
+ $sites = $this->getDb()->fetchAll("SELECT * FROM " . $this->table . "
+ WHERE `group` = ?", $group);
+
+ return $sites;
+ }
+
+ /**
+ * Returns the list of website groups, including the empty group
+ * if no group were specified for some websites
+ *
+ * @return array of group names strings
+ */
+ public function getSitesGroups()
+ {
+ $groups = $this->getDb()->fetchAll("SELECT DISTINCT `group` FROM " . $this->table);
+
+ $cleanedGroups = array();
+ foreach ($groups as $group) {
+ $cleanedGroups[] = $group['group'];
+ }
+
+ return $cleanedGroups;
+ }
+
+ /**
+ * Returns all websites
+ *
+ * @return array The list of websites, indexed by idsite
+ */
+ public function getAllSites()
+ {
+ $sites = $this->getDb()->fetchAll("SELECT * FROM " . $this->table . " ORDER BY idsite ASC");
+
+ return $sites;
+ }
+
+ /**
+ * Returns the list of the website IDs that received some visits since the specified timestamp.
+ *
+ * @param \Piwik\Date $time
+ * @param \Piwik\Date $now
+ * @return array The list of website IDs
+ */
+ public function getSitesWithVisits($time, $now)
+ {
+ $sites = Db::fetchAll("
+ SELECT idsite FROM " . $this->table . " s
+ WHERE EXISTS (
+ SELECT 1
+ FROM " . Common::prefixTable('log_visit') . " v
+ WHERE v.idsite = s.idsite
+ AND visit_last_action_time > ?
+ AND visit_last_action_time <= ?
+ LIMIT 1)
+ ", array($time, $now));
+
+ return $sites;
+ }
+
+
+ /**
+ * Returns the list of websites ID associated with a URL.
+ *
+ * @param string $url
+ * @param string $urlBis
+ * @return array list of websites ID
+ */
+ public function getAllSitesIdFromSiteUrl($url, $urlBis)
+ {
+ $siteUrlTable = Common::prefixTable('site_url');
+
+ $ids = $this->getDb()->fetchAll(
+ 'SELECT idsite FROM ' . $this->table . '
+ WHERE (main_url = ? OR main_url = ?) ' .
+ 'UNION
+ SELECT idsite FROM ' . $siteUrlTable . '
+ WHERE (url = ? OR url = ?) ', array($url, $urlBis, $url, $urlBis));
+
+ return $ids;
+ }
+
+ /**
+ * Returns the list of websites ID associated with a URL.
+ *
+ * @param string $url
+ * @return array list of websites ID
+ */
+ public function getSitesIdFromSiteUrlHavingAccess($url, $urlBis, $login)
+ {
+ $siteUrlTable = Common::prefixTable('site_url');
+ $sqlAccessSite = Access::getSqlAccessSite('idsite');
+
+ $ids = $this->getDb()->fetchAll(
+ 'SELECT idsite
+ FROM ' . $this->table . '
+ WHERE (main_url = ? OR main_url = ?)' .
+ 'AND idsite IN (' . $sqlAccessSite . ') ' .
+ 'UNION
+ SELECT idsite
+ FROM ' . $siteUrlTable . '
+ WHERE (url = ? OR url = ?)' .
+ 'AND idsite IN (' . $sqlAccessSite . ')',
+ array($url, $urlBis, $login, $url, $urlBis, $login));
+
+ return $ids;
+ }
+
+ /**
+ * Returns all websites with a timezone matching one the specified timezones
+ *
+ * @param array $timezones
+ * @return array
+ * @ignore
+ */
+ public function getSitesFromTimezones($timezones)
+ {
+ $query = 'SELECT idsite FROM ' . $this->table . '
+ WHERE timezone IN (' . Common::getSqlStringFieldsArray($timezones) . ')
+ ORDER BY idsite ASC';
+ $sites = $this->getDb()->fetchAll($query, $timezones);
+
+ return $sites;
+ }
+
+ public function deleteSite($idSite)
+ {
+ $db = $this->getDb();
+
+ $db->query("DELETE FROM " . $this->table . " WHERE idsite = ?", $idSite);
+ $db->query("DELETE FROM " . Common::prefixTable("site_url") . " WHERE idsite = ?", $idSite);
+ $db->query("DELETE FROM " . Common::prefixTable("access") . " WHERE idsite = ?", $idSite);
+ }
+
/**
* Returns the list of websites from the ID array in parameters.
*
@@ -36,11 +195,10 @@ class Model
$idSites = array_map('intval', $idSites);
- $db = Db::get();
- $sites = $db->fetchAll("SELECT *
- FROM " . Common::prefixTable("site") . "
- WHERE idsite IN (" . implode(", ", $idSites) . ")
- ORDER BY idsite ASC $limit");
+ $db = $this->getDb();
+ $sites = $db->fetchAll("SELECT * FROM " . $this->table . "
+ WHERE idsite IN (" . implode(", ", $idSites) . ")
+ ORDER BY idsite ASC $limit");
return $sites;
}
@@ -54,9 +212,8 @@ class Model
*/
public function getSiteFromId($idSite)
{
- $site = Db::get()->fetchRow("SELECT *
- FROM " . Common::prefixTable("site") . "
- WHERE idsite = ?", $idSite);
+ $site = $this->getDb()->fetchRow("SELECT * FROM " . $this->table . "
+ WHERE idsite = ?", $idSite);
return $site;
}
@@ -69,11 +226,13 @@ class Model
*/
public function getSitesId()
{
- $result = Db::fetchAll("SELECT idsite FROM " . Common::prefixTable('site'));
+ $result = Db::fetchAll("SELECT idsite FROM " . Common::prefixTable('site'));
+
$idSites = array();
foreach ($result as $idSite) {
$idSites[] = $idSite['idsite'];
}
+
return $idSites;
}
@@ -87,7 +246,6 @@ class Model
public function getSiteUrlsFromId($idSite)
{
$urls = $this->getAliasSiteUrlsFromId($idSite);
-
$site = $this->getSiteFromId($idSite);
if (empty($site)) {
@@ -106,14 +264,119 @@ class Model
*/
public function getAliasSiteUrlsFromId($idSite)
{
- $db = Db::get();
- $result = $db->fetchAll("SELECT url
- FROM " . Common::prefixTable("site_url") . "
- WHERE idsite = ?", $idSite);
+ $db = $this->getDb();
+ $result = $db->fetchAll("SELECT url FROM " . Common::prefixTable("site_url") . "
+ WHERE idsite = ?", $idSite);
$urls = array();
foreach ($result as $url) {
$urls[] = $url['url'];
}
+
return $urls;
}
+
+ public function updateSite($site, $idSite)
+ {
+ $idSite = (int) $idSite;
+
+ $db = $this->getDb();
+ $db->update($this->table, $site, "idsite = $idSite");
+ }
+
+ /**
+ * Returns the list of unique timezones from all configured sites.
+ *
+ * @return array ( string )
+ */
+ public function getUniqueSiteTimezones()
+ {
+ $results = Db::fetchAll("SELECT distinct timezone FROM " . $this->table);
+
+ $timezones = array();
+ foreach ($results as $result) {
+ $timezones[] = $result['timezone'];
+ }
+
+ return $timezones;
+ }
+
+ /**
+ * Updates the field ts_created for the specified websites.
+ *
+ * @param $idSites int Id Site to update ts_created
+ * @param string Date to set as creation date.
+ *
+ * @ignore
+ */
+ public function updateSiteCreatedTime($idSites, $minDateSql)
+ {
+ $idSites = array_map('intval', $idSites);
+
+ $query = "UPDATE " . $this->table . " SET ts_created = ?" .
+ " WHERE idsite IN ( " . implode(",", $idSites) . " ) AND ts_created > ?";
+
+ $bind = array($minDateSql, $minDateSql);
+
+ Db::query($query, $bind);
+ }
+
+ /**
+ * Insert the list of alias URLs for the website.
+ * The URLs must not exist already for this website!
+ */
+ public function insertSiteUrl($idSite, $url)
+ {
+ $db = $this->getDb();
+ $db->insert(Common::prefixTable("site_url"), array(
+ 'idsite' => (int) $idSite,
+ 'url' => $url
+ )
+ );
+ }
+
+ public function getPatternMatchSites($ids, $pattern, $limit)
+ {
+ $ids_str = '';
+ foreach ($ids as $id_val) {
+ $ids_str .= (int) $id_val . ' , ';
+ }
+ $ids_str .= (int) $id_val;
+
+ $bind = array('%' . $pattern . '%', 'http%' . $pattern . '%', '%' . $pattern . '%');
+
+ // Also match the idsite
+ $where = '';
+ if (is_numeric($pattern)) {
+ $bind[] = $pattern;
+ $where = 'ORs s.idsite = ?';
+ }
+
+ $query = "SELECT idsite, name, main_url, `group`
+ FROM " . $this->table . " s
+ WHERE ( s.name like ?
+ OR s.main_url like ?
+ OR s.`group` like ?
+ $where )
+ AND idsite in ($ids_str)
+ LIMIT " . (int) $limit;
+
+ $db = $this->getDb();
+ $sites = $db->fetchAll($query, $bind);
+
+ return $sites;
+ }
+
+ /**
+ * Delete all the alias URLs for the given idSite.
+ */
+ public function deleteSiteAliasUrls($idsite)
+ {
+ $db = $this->getDb();
+ $db->query("DELETE FROM " . Common::prefixTable("site_url") . " WHERE idsite = ?", $idsite);
+ }
+
+ private function getDb()
+ {
+ return Db::get();
+ }
}
diff --git a/plugins/Transitions/API.php b/plugins/Transitions/API.php
index faeddf9f0b..32abffdc06 100644
--- a/plugins/Transitions/API.php
+++ b/plugins/Transitions/API.php
@@ -172,7 +172,6 @@ class API extends \Piwik\Plugin\API
*/
private function addInternalReferrers($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping)
{
-
$data = $this->queryInternalReferrers($idaction, $actionType, $logAggregator, $limitBeforeGrouping);
if ($data['pageviews'] == 0) {
@@ -198,7 +197,6 @@ class API extends \Piwik\Plugin\API
*/
private function addFollowingActions($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping, $includeLoops = false)
{
-
$data = $this->queryFollowingActions(
$idaction, $actionType, $logAggregator, $limitBeforeGrouping, $includeLoops);
@@ -225,7 +223,7 @@ class API extends \Piwik\Plugin\API
if ($actionType != 'title') {
// specific setup for page urls
$types[Action::TYPE_PAGE_URL] = 'followingPages';
- $dimension = 'IF( idaction_url IS NULL, idaction_name, idaction_url )';
+ $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;
@@ -408,7 +406,7 @@ class API extends \Piwik\Plugin\API
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 )';
+ $dimension = 'if ( idaction_url_ref IS NULL, idaction_name_ref, idaction_url_ref )';
$joinLogActionOn = $dimension;
} else {
$joinLogActionOn = $dimension;
@@ -514,7 +512,6 @@ class API extends \Piwik\Plugin\API
*/
private function addExternalReferrers($logAggregator, &$report, $idaction, $actionType, $limitBeforeGrouping)
{
-
$data = $this->queryExternalReferrers(
$idaction, $actionType, $logAggregator, $limitBeforeGrouping);
diff --git a/plugins/UserCountry/Controller.php b/plugins/UserCountry/Controller.php
index 483b2d844f..a6ed712dbe 100644
--- a/plugins/UserCountry/Controller.php
+++ b/plugins/UserCountry/Controller.php
@@ -369,7 +369,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
private function dieIfGeolocationAdminIsDisabled()
{
- if(!UserCountry::isGeoLocationAdminEnabled()) {
+ if (!UserCountry::isGeoLocationAdminEnabled()) {
throw new \Exception('Geo location setting page has been disabled.');
}
}
diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php
index 61b26323da..7a1c9faf7a 100644
--- a/plugins/UsersManager/API.php
+++ b/plugins/UsersManager/API.php
@@ -318,7 +318,7 @@ class API extends \Piwik\Plugin\API
*
* @exception in case of an invalid parameter
*/
- public function addUser($userLogin, $password, $email, $alias = false)
+ public function addUser($userLogin, $password, $email, $alias = false, $_isPasswordHashed = false)
{
Piwik::checkUserHasSuperUserAccess();
@@ -326,10 +326,15 @@ class API extends \Piwik\Plugin\API
$this->checkEmail($email);
$password = Common::unsanitizeInputValue($password);
- UsersManager::checkPassword($password);
+ if (!$_isPasswordHashed) {
+ UsersManager::checkPassword($password);
+
+ $passwordTransformed = UsersManager::getPasswordHash($password);
+ } else {
+ $passwordTransformed = $password;
+ }
$alias = $this->getCleanAlias($alias, $userLogin);
- $passwordTransformed = UsersManager::getPasswordHash($password);
$token_auth = $this->getTokenAuth($userLogin, $passwordTransformed);
@@ -435,7 +440,7 @@ class API extends \Piwik\Plugin\API
$this->checkEmail($email);
}
- $alias = $this->getCleanAlias($alias, $userLogin);
+ $alias = $this->getCleanAlias($alias, $userLogin);
$token_auth = $this->getTokenAuth($userLogin, $password);
$this->model->updateUser($userLogin, $password, $email, $alias, $token_auth);
diff --git a/plugins/UsersManager/Controller.php b/plugins/UsersManager/Controller.php
index 56974b1efc..d22240b977 100644
--- a/plugins/UsersManager/Controller.php
+++ b/plugins/UsersManager/Controller.php
@@ -177,7 +177,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
);
// assertion
- if(count($dates) != count($mappingDatesToPeriods)) {
+ if (count($dates) != count($mappingDatesToPeriods)) {
throw new Exception("some metadata is missing in getDefaultDates()");
}
@@ -251,7 +251,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
Piwik::checkUserIsNotAnonymous();
$salt = Common::getRequestVar('ignoreSalt', false, 'string');
- if($salt !== $this->getIgnoreCookieSalt()) {
+ if ($salt !== $this->getIgnoreCookieSalt()) {
throw new Exception("Not authorized");
}
diff --git a/plugins/UsersManager/Menu.php b/plugins/UsersManager/Menu.php
index 8335d97305..88aeb051f5 100644
--- a/plugins/UsersManager/Menu.php
+++ b/plugins/UsersManager/Menu.php
@@ -9,6 +9,7 @@
namespace Piwik\Plugins\UsersManager;
use Piwik\Menu\MenuAdmin;
+use Piwik\Menu\MenuUser;
use Piwik\Piwik;
class Menu extends \Piwik\Plugin\Menu
@@ -20,4 +21,11 @@ class Menu extends \Piwik\Plugin\Menu
$menu->addManageItem('UsersManager_MenuUserSettings', $this->urlForAction('userSettings'), $order = 3);
}
}
+
+ public function configureUserMenu(MenuUser $menu)
+ {
+ if (!Piwik::isUserIsAnonymous()) {
+ $menu->addItem('', 'General_Settings', $this->urlForAction('index'), 0);
+ }
+ }
}
diff --git a/plugins/UsersManager/Model.php b/plugins/UsersManager/Model.php
index 872d693ee6..3d62ca61fd 100644
--- a/plugins/UsersManager/Model.php
+++ b/plugins/UsersManager/Model.php
@@ -26,6 +26,14 @@ use Piwik\Piwik;
*/
class Model
{
+ private static $rawPrefix = 'user';
+ private $table;
+
+ public function __construct()
+ {
+ $this->table = Common::prefixTable(self::$rawPrefix);
+ }
+
/**
* Returns the list of all the users
*
@@ -39,13 +47,12 @@ class Model
if (!empty($userLogins)) {
$where = 'WHERE login IN (' . Common::getSqlStringFieldsArray($userLogins) . ')';
- $bind = $userLogins;
+ $bind = $userLogins;
}
- $users = Db::get()->fetchAll("SELECT *
- FROM " . Common::prefixTable("user") . "
- $where
- ORDER BY login ASC", $bind);
+ $users = $this->getDb()->fetchAll("SELECT * FROM " . $this->table . "
+ $where
+ ORDER BY login ASC", $bind);
return $users;
}
@@ -57,9 +64,8 @@ class Model
*/
public function getUsersLogin()
{
- $users = Db::get()->fetchAll("SELECT login
- FROM " . Common::prefixTable("user") . "
- ORDER BY login ASC");
+ $users = $this->getDb()->fetchAll("SELECT login FROM " . $this->table . " ORDER BY login ASC");
+
$return = array();
foreach ($users as $login) {
$return[] = $login['login'];
@@ -70,10 +76,9 @@ class Model
public function getUsersSitesFromAccess($access)
{
- $users = Db::get()->fetchAll("SELECT login,idsite
- FROM " . Common::prefixTable("access")
- . " WHERE access = ?
- ORDER BY login, idsite", $access);
+ $users = $this->getDb()->fetchAll("SELECT login,idsite FROM " . Common::prefixTable("access")
+ . " WHERE access = ?
+ ORDER BY login, idsite", $access);
$return = array();
foreach ($users as $user) {
@@ -85,9 +90,8 @@ class Model
public function getUsersAccessFromSite($idSite)
{
- $users = Db::get()->fetchAll("SELECT login,access
- FROM " . Common::prefixTable("access")
- . " WHERE idsite = ?", $idSite);
+ $users = $this->getDb()->fetchAll("SELECT login,access FROM " . Common::prefixTable("access")
+ . " WHERE idsite = ?", $idSite);
$return = array();
foreach ($users as $user) {
@@ -99,9 +103,9 @@ class Model
public function getUsersLoginWithSiteAccess($idSite, $access)
{
- $users = Db::get()->fetchAll("SELECT login
- FROM " . Common::prefixTable("access")
- . " WHERE idsite = ? AND access = ?", array($idSite, $access));
+ $users = $this->getDb()->fetchAll("SELECT login
+ FROM " . Common::prefixTable("access")
+ . " WHERE idsite = ? AND access = ?", array($idSite, $access));
$logins = array();
foreach ($users as $user) {
@@ -129,9 +133,8 @@ class Model
*/
public function getSitesAccessFromUser($userLogin)
{
- $users = Db::get()->fetchAll("SELECT idsite,access
- FROM " . Common::prefixTable("access")
- . " WHERE login = ?", $userLogin);
+ $users = $this->getDb()->fetchAll("SELECT idsite,access FROM " . Common::prefixTable("access")
+ . " WHERE login = ?", $userLogin);
$return = array();
foreach ($users as $user) {
@@ -146,23 +149,20 @@ class Model
public function getUser($userLogin)
{
- return Db::get()->fetchRow("SELECT *
- FROM " . Common::prefixTable("user")
- . " WHERE login = ?", $userLogin);
+ return $this->getDb()->fetchRow("SELECT * FROM " . $this->table
+ . " WHERE login = ?", $userLogin);
}
public function getUserByEmail($userEmail)
{
- return Db::get()->fetchRow("SELECT *
- FROM " . Common::prefixTable("user")
- . " WHERE email = ?", $userEmail);
+ return $this->getDb()->fetchRow("SELECT * FROM " . $this->table
+ . " WHERE email = ?", $userEmail);
}
public function getUserByTokenAuth($tokenAuth)
{
- return Db::get()->fetchRow('SELECT *
- FROM ' . Common::prefixTable('user') . '
- WHERE token_auth = ?', $tokenAuth);
+ return $this->getDb()->fetchRow('SELECT * FROM ' . $this->table . '
+ WHERE token_auth = ?', $tokenAuth);
}
public function addUser($userLogin, $passwordTransformed, $email, $alias, $tokenAuth, $dateRegistered)
@@ -177,12 +177,12 @@ class Model
'superuser_access' => 0
);
- Db::get()->insert(Common::prefixTable("user"), $user);
+ $this->getDb()->insert($this->table, $user);
}
public function setSuperUserAccess($userLogin, $hasSuperUserAccess)
{
- Db::get()->update(Common::prefixTable("user"),
+ $this->getDb()->update($this->table,
array(
'superuser_access' => $hasSuperUserAccess ? 1 : 0
),
@@ -192,17 +192,17 @@ class Model
public function getUsersHavingSuperUserAccess()
{
- $users = Db::get()->fetchAll("SELECT login, email
- FROM " . Common::prefixTable("user") . "
- WHERE superuser_access = 1
- ORDER BY date_registered ASC");
+ $users = $this->getDb()->fetchAll("SELECT login, email
+ FROM " . Common::prefixTable("user") . "
+ WHERE superuser_access = 1
+ ORDER BY date_registered ASC");
return $users;
}
public function updateUser($userLogin, $password, $email, $alias, $tokenAuth)
{
- Db::get()->update(Common::prefixTable("user"),
+ $this->getDb()->update($this->table,
array(
'password' => $password,
'alias' => $alias,
@@ -215,24 +215,22 @@ class Model
public function userExists($userLogin)
{
- $count = Db::get()->fetchOne("SELECT count(*)
- FROM " . Common::prefixTable("user") . "
- WHERE login = ?", $userLogin);
+ $count = $this->getDb()->fetchOne("SELECT count(*) FROM " . $this->table . "
+ WHERE login = ?", $userLogin);
return $count != 0;
}
public function userEmailExists($userEmail)
{
- $count = Db::get()->fetchOne("SELECT count(*)
- FROM " . Common::prefixTable("user") . "
- WHERE email = ?", $userEmail);
+ $count = $this->getDb()->fetchOne("SELECT count(*) FROM " . $this->table . "
+ WHERE email = ?", $userEmail);
return $count != 0;
}
public function addUserAccess($userLogin, $access, $idSites)
{
foreach ($idSites as $idsite) {
- Db::get()->insert(Common::prefixTable("access"),
+ $this->getDb()->insert(Common::prefixTable("access"),
array("idsite" => $idsite,
"login" => $userLogin,
"access" => $access)
@@ -242,7 +240,7 @@ class Model
public function deleteUserOnly($userLogin)
{
- Db::get()->query("DELETE FROM " . Common::prefixTable("user") . " WHERE login = ?", $userLogin);
+ $this->getDb()->query("DELETE FROM " . $this->table . " WHERE login = ?", $userLogin);
/**
* Triggered after a user has been deleted.
@@ -258,12 +256,12 @@ class Model
public function deleteUserAccess($userLogin, $idSites = null)
{
if (is_null($idSites)) {
- Db::get()->query("DELETE FROM " . Common::prefixTable("access") .
+ $this->getDb()->query("DELETE FROM " . Common::prefixTable("access") .
" WHERE login = ?",
array($userLogin));
} else {
foreach ($idSites as $idsite) {
- Db::get()->query("DELETE FROM " . Common::prefixTable("access") .
+ $this->getDb()->query("DELETE FROM " . Common::prefixTable("access") .
" WHERE idsite = ? AND login = ?",
array($idsite, $userLogin)
);
@@ -271,4 +269,9 @@ class Model
}
}
+ private function getDb()
+ {
+ return Db::get();
+ }
+
}
diff --git a/plugins/UsersManager/UsersManager.php b/plugins/UsersManager/UsersManager.php
index b2c4b2e8f3..cdc383d041 100644
--- a/plugins/UsersManager/UsersManager.php
+++ b/plugins/UsersManager/UsersManager.php
@@ -34,7 +34,8 @@ class UsersManager extends \Piwik\Plugin
'SitesManager.deleteSite.end' => 'deleteSite',
'Tracker.Cache.getSiteAttributes' => 'recordAdminUsersInCache',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
- 'Platform.initialized' => 'onPlatformInitialized'
+ 'Platform.initialized' => 'onPlatformInitialized',
+ 'CronArchive.getTokenAuth' => 'getCronArchiveTokenAuth'
);
}
@@ -62,9 +63,22 @@ class UsersManager extends \Piwik\Plugin
foreach ($users as $user) {
$tokens[] = $user['token_auth'];
}
+
$attributes['admin_token_auth'] = $tokens;
}
+ public function getCronArchiveTokenAuth(&$token)
+ {
+ $model = new Model();
+ $superUsers = $model->getUsersHavingSuperUserAccess();
+
+ if (!empty($superUsers)) {
+ $superUser = array_shift($superUsers);
+
+ $token = $superUser['token_auth'];
+ }
+ }
+
/**
* Delete user preferences associated with a particular site
*/
@@ -105,7 +119,9 @@ class UsersManager extends \Piwik\Plugin
) {
return true;
}
+
$l = strlen($input);
+
return $l >= self::PASSWORD_MIN_LENGTH && $l <= self::PASSWORD_MAX_LENGTH;
}
diff --git a/plugins/VisitorGenerator b/plugins/VisitorGenerator
-Subproject 90327ea31c298d17cf14312d4eeefad7228c54a
+Subproject 5d6355fcb38e7b8964d697da3cf9e2d201bf224
diff --git a/plugins/VisitsSummary/Controller.php b/plugins/VisitsSummary/Controller.php
index 903b2477cf..63fe8ef205 100644
--- a/plugins/VisitsSummary/Controller.php
+++ b/plugins/VisitsSummary/Controller.php
@@ -159,7 +159,7 @@ class Controller extends \Piwik\Plugin\Controller
$view->maxActions = (int)$dataRow->getColumn('max_actions');
$view->nbActionsPerVisit = $dataRow->getColumn('nb_actions_per_visit');
- if(Common::isActionsPluginEnabled()) {
+ if (Common::isActionsPluginEnabled()) {
$view->showActionsPluginReports = true;
$dataTableActions = APIActions::getInstance()->get($idSite, Common::getRequestVar('period'), Common::getRequestVar('date'),
\Piwik\API\Request::getRawSegmentFromRequest());
diff --git a/tests/PHPUnit/Core/AssetManagerTest.php b/tests/PHPUnit/Core/AssetManagerTest.php
index 2e2f27f498..85fc6d9764 100644
--- a/tests/PHPUnit/Core/AssetManagerTest.php
+++ b/tests/PHPUnit/Core/AssetManagerTest.php
@@ -420,7 +420,7 @@ class AssetManagerTest extends PHPUnit_Framework_TestCase
return
'<script type="text/javascript">' . PHP_EOL .
'var translations = [];' . PHP_EOL .
- 'if(typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }for(var i in translations) { piwik_translations[i] = translations[i];} ' . PHP_EOL .
+ 'if (typeof(piwik_translations) == \'undefined\') { var piwik_translations = new Object; }for(var i in translations) { piwik_translations[i] = translations[i];} ' . PHP_EOL .
'</script>';
}
diff --git a/tests/PHPUnit/Core/Unzip/relative.zip b/tests/PHPUnit/Core/Http/fixture.zip
index 2388859d52..2388859d52 100644
--- a/tests/PHPUnit/Core/Unzip/relative.zip
+++ b/tests/PHPUnit/Core/Http/fixture.zip
Binary files differ
diff --git a/tests/PHPUnit/Core/HttpTest.php b/tests/PHPUnit/Core/HttpTest.php
index 36af08b943..af2711c530 100644
--- a/tests/PHPUnit/Core/HttpTest.php
+++ b/tests/PHPUnit/Core/HttpTest.php
@@ -19,9 +19,9 @@ class HttpTest extends PHPUnit_Framework_TestCase
public function getMethodsToTest()
{
return array(
- array('curl'),
- array('fopen'),
- array('socket'),
+ 'curl' => array('curl'),
+ 'fopen' => array('fopen'),
+ 'socket' => array('socket'),
);
}
@@ -33,7 +33,7 @@ class HttpTest extends PHPUnit_Framework_TestCase
public function testFetchRemoteFile($method)
{
$this->assertNotNull(Http::getTransportMethod());
- $result = Http::sendHttpRequestBy($method, 'http://localhost/piwik.js', 30);
+ $result = Http::sendHttpRequestBy($method, Fixture::getRootUrl() . 'piwik.js', 30);
$this->assertTrue(strpos($result, 'Piwik') !== false);
}
@@ -43,7 +43,7 @@ class HttpTest extends PHPUnit_Framework_TestCase
public function testFetchApiLatestVersion()
{
$destinationPath = PIWIK_USER_PATH . '/tmp/latest/LATEST';
- Http::fetchRemoteFile('http://localhost/', $destinationPath, 3);
+ Http::fetchRemoteFile(Fixture::getRootUrl(), $destinationPath, 3);
$this->assertFileExists($destinationPath);
$this->assertGreaterThan(0, filesize($destinationPath));
}
@@ -54,7 +54,7 @@ class HttpTest extends PHPUnit_Framework_TestCase
public function testFetchLatestZip()
{
$destinationPath = PIWIK_USER_PATH . '/tmp/latest/latest.zip';
- Http::fetchRemoteFile('http://localhost/tests/PHPUnit/Core/Unzip/relative.zip', $destinationPath, 3, 30);
+ Http::fetchRemoteFile(Fixture::getRootUrl() . 'tests/PHPUnit/Core/Http/fixture.zip', $destinationPath, 3, 30);
$this->assertFileExists($destinationPath);
$this->assertGreaterThan(0, filesize($destinationPath));
}
@@ -100,7 +100,7 @@ class HttpTest extends PHPUnit_Framework_TestCase
$result = Http::sendHttpRequestBy(
$method,
- 'http://localhost/tests/PHPUnit/Core/Unzip/relative.zip',
+ Fixture::getRootUrl() . 'tests/PHPUnit/Core/Http/fixture.zip',
30,
$userAgent = null,
$destinationPath = null,
diff --git a/tests/PHPUnit/Core/Unzip/empty.zip b/tests/PHPUnit/Core/Unzip/empty.zip
deleted file mode 100755
index e69de29bb2..0000000000
--- a/tests/PHPUnit/Core/Unzip/empty.zip
+++ /dev/null
diff --git a/tests/PHPUnit/Core/Unzip/test.gz b/tests/PHPUnit/Core/Unzip/test.gz
deleted file mode 100755
index 113cbed07f..0000000000
--- a/tests/PHPUnit/Core/Unzip/test.gz
+++ /dev/null
Binary files differ
diff --git a/tests/PHPUnit/Core/Unzip/test.tar.gz b/tests/PHPUnit/Core/Unzip/test.tar.gz
deleted file mode 100755
index b9cc91965d..0000000000
--- a/tests/PHPUnit/Core/Unzip/test.tar.gz
+++ /dev/null
Binary files differ
diff --git a/tests/PHPUnit/Core/Unzip/zaabs.zip b/tests/PHPUnit/Core/Unzip/zaabs.zip
deleted file mode 100644
index c5517f940d..0000000000
--- a/tests/PHPUnit/Core/Unzip/zaabs.zip
+++ /dev/null
Binary files differ
diff --git a/tests/PHPUnit/Core/Unzip/zaatt.zip b/tests/PHPUnit/Core/Unzip/zaatt.zip
deleted file mode 100644
index 91e0c4864a..0000000000
--- a/tests/PHPUnit/Core/Unzip/zaatt.zip
+++ /dev/null
Binary files differ
diff --git a/tests/PHPUnit/Core/UnzipTest.php b/tests/PHPUnit/Core/UnzipTest.php
deleted file mode 100644
index 12f754c8b7..0000000000
--- a/tests/PHPUnit/Core/UnzipTest.php
+++ /dev/null
@@ -1,217 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-use Piwik\Unzip\Gzip;
-use Piwik\Unzip;
-use Piwik\Unzip\PclZip;
-use Piwik\Unzip\Tar;
-use Piwik\Unzip\ZipArchive;
-
-class UnzipTest extends PHPUnit_Framework_TestCase
-{
- /**
- * @group Core
- */
- public function testRelativePath()
- {
- clearstatcache();
- $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
- $test = 'relative';
- $filename = dirname(__FILE__) . '/Unzip/' . $test . '.zip';
-
- if (class_exists('ZipArchive', false)) {
- $unzip = Unzip::factory('ZipArchive', $filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(1, count($res));
- $this->assertFileExists($extractDir . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/../../tests/' . $test . '.txt');
- unlink($extractDir . $test . '.txt');
-
- $unzip = new ZipArchive($filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(1, count($res));
- $this->assertFileExists($extractDir . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/../../tests/' . $test . '.txt');
- unlink($extractDir . $test . '.txt');
- }
-
- $unzip = Unzip::factory('PclZip', $filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(1, count($res));
- $this->assertFileExists($extractDir . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/../../tests/' . $test . '.txt');
- unlink($extractDir . $test . '.txt');
-
- $unzip = new PclZip($filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(1, count($res));
- $this->assertFileExists($extractDir . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/../../tests/' . $test . '.txt');
- unlink($extractDir . $test . '.txt');
- }
-
- /**
- * @group Core
- */
- public function testRelativePathAttack()
- {
- clearstatcache();
- $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
- $test = 'zaatt';
- $filename = dirname(__FILE__) . '/Unzip/' . $test . '.zip';
-
- if (class_exists('ZipArchive', false)) {
- $unzip = new ZipArchive($filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(0, $res);
- $this->assertFileNotExists($extractDir . $test . '.txt');
- $this->assertFileNotExists($extractDir . '../' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/../' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/../../' . $test . '.txt');
- }
-
- $unzip = new PclZip($filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(0, $res);
- $this->assertFileNotExists($extractDir . $test . '.txt');
- $this->assertFileNotExists($extractDir . '../' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/../' . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/../../' . $test . '.txt');
- }
-
- /**
- * @group Core
- */
- public function testAbsolutePathAttack()
- {
- clearstatcache();
- $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
- $test = 'zaabs';
- $filename = dirname(__FILE__) . '/Unzip/' . $test . '.zip';
-
- if (class_exists('ZipArchive', false)) {
- $unzip = new ZipArchive($filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(0, $res);
- $this->assertFileNotExists($extractDir . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt');
- }
-
- $unzip = new PclZip($filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(0, $res);
- $this->assertFileNotExists($extractDir . $test . '.txt');
- $this->assertFileNotExists(dirname(__FILE__) . '/' . $test . '.txt');
- }
-
- /**
- * @group Core
- */
- public function testUnzipErrorInfo()
- {
- clearstatcache();
- $filename = dirname(__FILE__) . '/Unzip/zaabs.zip';
-
- $unzip = new ZipArchive($filename);
- $this->assertContains('No error', $unzip->errorInfo());
- }
-
- /**
- * @group Core
- */
- public function testUnzipEmptyFile()
- {
- clearstatcache();
- $filename = dirname(__FILE__) . '/Unzip/empty.zip';
- $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
-
- $unzip = new ZipArchive($filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(0, $res);
- }
-
- /**
- * @group Core
- */
- public function testUnzipNotExistingFile()
- {
- clearstatcache();
- $filename = dirname(__FILE__) . '/Unzip/NotExisting.zip';
-
- try {
- new ZipArchive($filename);
- } catch (Exception $e) {
- return;
- }
- $this->fail('Exception not raised');
- }
-
- /**
- * @group Core
- */
- public function testUnzipInvalidFile2()
- {
- clearstatcache();
- $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
- $filename = dirname(__FILE__) . '/Unzip/NotExisting.zip';
-
- $unzip = new PclZip($filename);
- $res = $unzip->extract($extractDir);
- $this->assertEquals(0, $res);
-
- $this->assertContains('PCLZIP_ERR_MISSING_FILE', $unzip->errorInfo());
- }
-
- /**
- * @group Core
- */
- public function testGzipFile()
- {
- $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
- $extractFile = $extractDir . 'testgz.txt';
- $filename = dirname(__FILE__) . '/Unzip/test.gz';
-
- $unzip = new Gzip($filename);
- $res = $unzip->extract($extractFile);
- $this->assertTrue($res);
-
- $this->assertFileContentsEquals('TESTSTRING', $extractFile);
- }
-
- /**
- * @group Core
- */
- public function testTarGzFile()
- {
- $extractDir = PIWIK_USER_PATH . '/tmp/latest/';
- $filename = dirname(__FILE__) . '/Unzip/test.tar.gz';
-
- $unzip = new Tar($filename, 'gz');
- $res = $unzip->extract($extractDir);
- $this->assertTrue($res);
-
- $this->assertFileContentsEquals('TESTDATA', $extractDir . 'tarout1.txt');
- $this->assertFileContentsEquals('MORETESTDATA', $extractDir . 'tardir/tarout2.txt');
- }
-
- private function assertFileContentsEquals($expectedContent, $path)
- {
- $this->assertTrue(file_exists($path));
-
- $fd = fopen($path, 'rb');
- $actualContent = fread($fd, filesize($path));
- fclose($fd);
-
- $this->assertEquals($expectedContent, $actualContent);
- }
-}
diff --git a/tests/PHPUnit/DatabaseTestCase.php b/tests/PHPUnit/DatabaseTestCase.php
index ef247e0f1e..715571eba4 100644
--- a/tests/PHPUnit/DatabaseTestCase.php
+++ b/tests/PHPUnit/DatabaseTestCase.php
@@ -5,8 +5,10 @@
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
+use Piwik\Config;
use Piwik\Db;
use Piwik\Tests\Fixture;
+use Piwik\Tests\IntegrationTestCase;
/**
* Tests extending DatabaseTestCase are much slower to run: the setUp will
@@ -16,12 +18,48 @@ use Piwik\Tests\Fixture;
* then test it.
*
*/
-class DatabaseTestCase extends PHPUnit_Framework_TestCase
+class DatabaseTestCase extends IntegrationTestCase
{
/**
* @var Fixture
*/
- protected $fixture = null;
+ public static $fixture;
+ public static $tableData;
+
+ /**
+ * Implementation details:
+ *
+ * To increase speed of tests, database setup is done once in setUpBeforeClass.
+ * Afterwards, the content of the tables is stored in a static class variable,
+ * self::$tableData. Before each individual test, the database tables are
+ * truncated and the data in self::$tableData is restored.
+ *
+ * If your test modifies table columns, you will need to recreate the database
+ * completely. This can be accomplished by:
+ *
+ * public function setUp()
+ * {
+ * self::$fixture->performSetUp();
+ * }
+ *
+ * public function tearDown()
+ * {
+ * parent::tearDown();
+ * self::$fixture->performTearDown();
+ * }
+ */
+ public static function setUpBeforeClass()
+ {
+ static::configureFixture(static::$fixture);
+ parent::setUpBeforeClass();
+
+ self::$tableData = self::getDbTablesWithData();
+ }
+
+ public static function tearDownAfterClass()
+ {
+ self::$tableData = array();
+ }
/**
* Setup the database and create the base tables for all tests
@@ -30,9 +68,11 @@ class DatabaseTestCase extends PHPUnit_Framework_TestCase
{
parent::setUp();
- $this->fixture = new Fixture();
- $this->configureFixture();
- $this->fixture->performSetUp();
+ Config::getInstance()->setTestEnvironment();
+
+ if (!empty(self::$tableData)) {
+ self::restoreDbTables(self::$tableData);
+ }
}
/**
@@ -40,14 +80,17 @@ class DatabaseTestCase extends PHPUnit_Framework_TestCase
*/
public function tearDown()
{
+ self::$fixture->clearInMemoryCaches();
+
parent::tearDown();
- $this->fixture->performTearDown();
}
- protected function configureFixture()
+ protected static function configureFixture($fixture)
{
- $this->fixture->loadTranslations = false;
- $this->fixture->createSuperUser = false;
- $this->fixture->configureComponents = false;
+ $fixture->loadTranslations = false;
+ $fixture->createSuperUser = false;
+ $fixture->configureComponents = false;
}
}
+
+DatabaseTestCase::$fixture = new Fixture(); \ No newline at end of file
diff --git a/tests/PHPUnit/Fixture.php b/tests/PHPUnit/Fixture.php
index f625f98001..cc72b8498d 100644
--- a/tests/PHPUnit/Fixture.php
+++ b/tests/PHPUnit/Fixture.php
@@ -185,7 +185,7 @@ class Fixture extends PHPUnit_Framework_Assert
static::createAccessInstance();
// We need to be SU to create websites for tests
- Piwik::setUserHasSuperUserAccess();
+ Access::getInstance()->setSuperUserAccess();
Cache::deleteTrackerCache();
@@ -275,6 +275,11 @@ class Fixture extends PHPUnit_Framework_Assert
$this->dropDatabase();
}
+ $this->clearInMemoryCaches();
+ }
+
+ public function clearInMemoryCaches()
+ {
DataTableManager::getInstance()->deleteAll();
Option::clearCache();
Site::clearCache();
@@ -291,7 +296,7 @@ class Fixture extends PHPUnit_Framework_Assert
Config::unsetInstance();
\Piwik\Config::getInstance()->Plugins; // make sure Plugins exists in a config object for next tests that use Plugin\Manager
- // since Plugin\Manager uses getFromGlobalConfig which doesn't init the config object
+ // since Plugin\Manager uses getFromGlobalConfig which doesn't init the config object
}
public static function loadAllPlugins($testEnvironment = null, $testCaseClass = false, $extraPluginsToLoad = array())
diff --git a/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorIdAndUserId.php b/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorIdAndUserId.php
index 30accf676d..f039505460 100644
--- a/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorIdAndUserId.php
+++ b/tests/PHPUnit/Fixtures/FewVisitsWithSetVisitorIdAndUserId.php
@@ -80,12 +80,20 @@ class FewVisitsWithSetVisitorId extends Fixture
private function trackVisits_setUserId()
{
+ $userId = self::USER_ID_EXAMPLE_COM;
// total = 2 visitors, 3 page views
$t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
// First, some basic tests
$this->settingInvalidUserIdShouldThrow($t);
+ // We create a visit with no User ID.
+ // When User ID will be set below, then it will UPDATE this visit here that starts without UserID
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(1.9)->getDatetime());
+ $t->setVisitorId('6be121d126d93581');
+ $t->setUrl('http://example.org/no-user-id-set-but-should-appear-in-user-id-visit');
+ self::checkResponse($t->doTrackPageView('no User Id set but it should appear in '. $userId .'!'));
+
// A NEW VISIT
// Setting both Visitor ID and User ID
// -> User ID takes precedence
@@ -98,22 +106,21 @@ class FewVisitsWithSetVisitorId extends Fixture
$this->assertEquals($generatedVisitorId, $t->getVisitorId());
// Set User ID
- $userId = self::USER_ID_EXAMPLE_COM;
$t->setUserId($userId);
- $this->assertEquals($userId, $t->getUserId());
// User ID takes precedence over any previously set Visitor ID
- $hashUserId = $t->getUserIdHashed($userId);
- $this->assertEquals($hashUserId, $t->getVisitorId());
+ $this->assertEquals($t->getUserIdHashed($userId), $t->getVisitorId());
+ $this->assertEquals('9395988394d4568d', $t->getVisitorId());
+ $this->assertEquals($userId, $t->getUserId());
// Track a pageview with this user id
self::checkResponse($t->doTrackPageView('incredible title!'));
// Track another pageview
$t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(2.1)->getDatetime());
+ $this->assertEquals($userId, $t->getUserId());
self::checkResponse($t->doTrackPageView('second page'));
-
// A NEW VISIT WITH A SET USER ID
// Change User ID -> This will create a new visit
$t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(2.2)->getDatetime());
diff --git a/tests/PHPUnit/Fixtures/SqlDump.php b/tests/PHPUnit/Fixtures/SqlDump.php
index 7aea83b781..06c094de03 100644
--- a/tests/PHPUnit/Fixtures/SqlDump.php
+++ b/tests/PHPUnit/Fixtures/SqlDump.php
@@ -7,6 +7,7 @@
*/
namespace Piwik\Tests\Fixtures;
+use Piwik\Access;
use Piwik\ArchiveProcessor\Rules;
use Piwik\Config;
use Piwik\Db;
@@ -79,7 +80,7 @@ class SqlDump extends Fixture
Rules::setBrowserTriggerArchiving(true);
// reload access
- Piwik::setUserHasSuperUserAccess();
+ Access::getInstance()->reloadAccess();
$this->getTestEnvironment()->configOverride = array(
'database' => array(
diff --git a/tests/PHPUnit/Integration/ArchiveInvalidationTest.php b/tests/PHPUnit/Integration/ArchiveInvalidationTest.php
index a45aaac72c..1d04920a8c 100644
--- a/tests/PHPUnit/Integration/ArchiveInvalidationTest.php
+++ b/tests/PHPUnit/Integration/ArchiveInvalidationTest.php
@@ -86,7 +86,6 @@ class ArchiveInvalidationTest extends IntegrationTestCase
$this->invalidateTestArchives();
$this->runApiTests($api, $params);
-
}
/**
diff --git a/tests/PHPUnit/Integration/Core/AccessTest.php b/tests/PHPUnit/Integration/Core/AccessTest.php
index 06a9b4a036..ec1eb9bd29 100644
--- a/tests/PHPUnit/Integration/Core/AccessTest.php
+++ b/tests/PHPUnit/Integration/Core/AccessTest.php
@@ -11,7 +11,6 @@ use Piwik\AuthResult;
/**
* Class Core_AccessTest
*
- * @group Core_AccessTest
* @group Core
*/
class Core_AccessTest extends DatabaseTestCase
@@ -308,4 +307,58 @@ class Core_AccessTest extends DatabaseTestCase
$this->assertTrue($access->reloadAccess($mock));
$this->assertFalse($access->hasSuperUserAccess());
}
+
+ public function test_doAsSuperUser_ChangesSuperUserAccessCorrectly()
+ {
+ Access::getInstance()->setSuperUserAccess(false);
+
+ $this->assertFalse(Access::getInstance()->hasSuperUserAccess());
+
+ Access::doAsSuperUser(function () {
+ Core_AccessTest::assertTrue(Access::getInstance()->hasSuperUserAccess());
+ });
+
+ $this->assertFalse(Access::getInstance()->hasSuperUserAccess());
+ }
+
+ public function test_doAsSuperUser_RemovesSuperUserAccess_IfExceptionThrown()
+ {
+ Access::getInstance()->setSuperUserAccess(false);
+
+ $this->assertFalse(Access::getInstance()->hasSuperUserAccess());
+
+ try {
+ Access::doAsSuperUser(function () {
+ throw new Exception();
+ });
+
+ $this->fail("Exception was not propagated by doAsSuperUser.");
+ } catch (Exception $ex)
+ {
+ // pass
+ }
+
+ $this->assertFalse(Access::getInstance()->hasSuperUserAccess());
+ }
+
+ public function test_doAsSuperUser_ReturnsCallbackResult()
+ {
+ $result = Access::doAsSuperUser(function () {
+ return 24;
+ });
+ $this->assertEquals(24, $result);
+ }
+
+ public function test_reloadAccess_DoesNotRemoveSuperUserAccess_IfUsedInDoAsSuperUser()
+ {
+ Access::getInstance()->setSuperUserAccess(false);
+
+ Access::doAsSuperUser(function () {
+ $access = Access::getInstance();
+
+ Core_AccessTest::assertTrue($access->hasSuperUserAccess());
+ $access->reloadAccess();
+ Core_AccessTest::assertTrue($access->hasSuperUserAccess());
+ });
+ }
} \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/Core/CronArchive/SharedSiteIdsTest.php b/tests/PHPUnit/Integration/Core/CronArchive/SharedSiteIdsTest.php
index 5183177aa9..eca1d85aea 100644
--- a/tests/PHPUnit/Integration/Core/CronArchive/SharedSiteIdsTest.php
+++ b/tests/PHPUnit/Integration/Core/CronArchive/SharedSiteIdsTest.php
@@ -10,6 +10,7 @@ use Piwik\CronArchive\SharedSiteIds;
/**
* @group Core
+ * @group SharedSiteIdsTest
*/
class SharedSiteIdsTest extends DatabaseTestCase
{
@@ -21,7 +22,6 @@ class SharedSiteIdsTest extends DatabaseTestCase
public function setUp()
{
parent::setUp();
- $this->fixture->performSetUp(true);
$this->sharedSiteIds = new SharedSiteIds(array(1,2,5,9));
}
diff --git a/tests/PHPUnit/Integration/Core/LogTest.php b/tests/PHPUnit/Integration/Core/LogTest.php
index f7d35e451b..a3923183ae 100644
--- a/tests/PHPUnit/Integration/Core/LogTest.php
+++ b/tests/PHPUnit/Integration/Core/LogTest.php
@@ -31,9 +31,9 @@ class Core_LogTest extends DatabaseTestCase
'screen' => 'dummy error message<br />
<br />
--&gt; To temporarily debug this error further, set const PIWIK_PRINT_ERROR_BACKTRACE=true; in index.php',
- 'file' => '[Core_LogTest] LogTest.php(161): dummy error message
+ 'file' => '[Core_LogTest] LogTest.php(165): dummy error message
dummy backtrace',
- 'database' => '[Core_LogTest] LogTest.php(161): dummy error message
+ 'database' => '[Core_LogTest] LogTest.php(165): dummy error message
dummy backtrace'
);
@@ -55,6 +55,8 @@ dummy backtrace'
public static function setUpBeforeClass()
{
+ parent::setUpBeforeClass();
+
Error::setErrorHandler();
ExceptionHandler::setUp();
}
@@ -63,6 +65,8 @@ dummy backtrace'
{
restore_error_handler();
restore_exception_handler();
+
+ parent::tearDownAfterClass();
}
public function setUp()
diff --git a/tests/PHPUnit/Integration/Core/OptionTest.php b/tests/PHPUnit/Integration/Core/OptionTest.php
index c926e1c808..41a12df3fd 100644
--- a/tests/PHPUnit/Integration/Core/OptionTest.php
+++ b/tests/PHPUnit/Integration/Core/OptionTest.php
@@ -13,6 +13,7 @@ use Piwik\Option;
* Class Core_OptionTest
*
* @group Core
+ * @group Core_OptionTest
*/
class Core_OptionTest extends DatabaseTestCase
{
diff --git a/tests/PHPUnit/Integration/Core/SegmentTest.php b/tests/PHPUnit/Integration/Core/SegmentTest.php
index ce7a0c363f..3fddab94b2 100644
--- a/tests/PHPUnit/Integration/Core/SegmentTest.php
+++ b/tests/PHPUnit/Integration/Core/SegmentTest.php
@@ -8,8 +8,10 @@
use Piwik\Access;
use Piwik\Common;
use Piwik\Segment;
-use Piwik\Tests\Fixture;
+/**
+ * @group SegmentTest
+ */
class SegmentTest extends DatabaseTestCase
{
public function setUp()
@@ -20,14 +22,11 @@ class SegmentTest extends DatabaseTestCase
$pseudoMockAccess = new FakeAccess;
FakeAccess::$superUser = true;
Access::setSingletonInstance($pseudoMockAccess);
-
- Fixture::loadAllPlugins();
}
public function tearDown()
{
parent::tearDown();
- Fixture::unloadAllPlugins();
}
protected function _filterWhitsSpaces($valueToFilter)
diff --git a/tests/PHPUnit/Integration/Core/Tracker/DbTest.php b/tests/PHPUnit/Integration/Core/Tracker/DbTest.php
new file mode 100644
index 0000000000..ef738d5515
--- /dev/null
+++ b/tests/PHPUnit/Integration/Core/Tracker/DbTest.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+use Piwik\Common;
+use Piwik\Db;
+
+/**
+ * Tracker DB test
+ *
+ * @group Core
+ * @group TrackerDbTest
+ */
+class Core_Tracker_DbTest extends DatabaseTestCase
+{
+ public function test_rowCount_whenUpdating_returnsAllMatchedRowsNotOnlyUpdatedRows()
+ {
+ $db = \Piwik\Tracker::getDatabase();
+ // insert one record
+ $db->query("INSERT INTO `" . Common::prefixTable('option') . "` VALUES ('rowid', '1', false)");
+
+ // We will now UPDATE this table and check rowCount() value
+ $sqlUpdate = "UPDATE `" . Common::prefixTable('option') . "` SET option_value = 2";
+
+ // when no record was updated, return 0
+ $result = $db->query($sqlUpdate . " WHERE option_name = 'NOT FOUND'");
+ $this->assertSame(0, $db->rowCount($result));
+
+ // when one record was found and updated, returns 1
+ $result = $db->query($sqlUpdate . " WHERE option_name = 'rowid'");
+ $this->assertSame(1, $db->rowCount($result));
+
+ // when one record was found but NOT actually updated (as values have not changed), we make sure to return 1
+ // testing for MYSQLI_CLIENT_FOUND_ROWS and MYSQL_ATTR_FOUND_ROWS
+ $result = $db->query($sqlUpdate . " WHERE option_name = 'rowid'");
+ $this->assertSame(1, $db->rowCount($result));
+ }
+
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/Core/TrackerTest.php b/tests/PHPUnit/Integration/Core/TrackerTest.php
index ad2810f789..a5eb8cbcb8 100644
--- a/tests/PHPUnit/Integration/Core/TrackerTest.php
+++ b/tests/PHPUnit/Integration/Core/TrackerTest.php
@@ -16,13 +16,12 @@ class Core_TrackerTest extends DatabaseTestCase
public function setUp()
{
parent::setUp();
- \Piwik\Piwik::setUserHasSuperUserAccess(true);
Fixture::createWebsite('2014-02-04');
}
- protected function configureFixture()
+ protected static function configureFixture($fixture)
{
- $this->fixture->createSuperUser = true;
+ $fixture->createSuperUser = true;
}
/**
diff --git a/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php b/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php
index a7f41f6b3b..c7f3d9ca00 100755
--- a/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php
+++ b/tests/PHPUnit/Integration/ManyVisitorsOneWebsiteTest.php
@@ -108,6 +108,7 @@ class ManyVisitorsOneWebsiteTest extends IntegrationTestCase
// Randomly fails on 5.3
if(!self::isPhpVersion53()) {
+
$apiToTest[] = array('Live.getLastVisitsDetails', array(
'idSite' => $idSite,
'date' => $dateString,
@@ -140,7 +141,11 @@ class ManyVisitorsOneWebsiteTest extends IntegrationTestCase
'date' => $dateString,
'periods' => 'month',
'testSuffix' => '_Live.getLastVisitsDetails_sortByIdVisitAsc',
- 'otherRequestParameters' => array('filter_sort_order' => 'asc', 'filter_sort_column' => 'idVisit', 'filter_limit' => 7)
+ 'otherRequestParameters' => array('filter_sort_order' => 'asc',
+ 'filter_sort_column' => 'idVisit',
+ 'filter_limit' => 7,
+ 'hideColumns' => 'latitude,longitude' // Mysqli has troubles with lat/long rounding
+ )
));
}
diff --git a/tests/PHPUnit/Integration/PivotByQueryParamTest.php b/tests/PHPUnit/Integration/PivotByQueryParamTest.php
index b668a68f7a..7e579f4b5e 100644
--- a/tests/PHPUnit/Integration/PivotByQueryParamTest.php
+++ b/tests/PHPUnit/Integration/PivotByQueryParamTest.php
@@ -43,7 +43,7 @@ class PivotByQueryParamTest extends IntegrationTestCase
));
}
- public function test_PivotBySubtableDimension_CreatesCorrectPivotTable_WhenEntireHirearchyIsNotLoaded()
+ public function test_PivotBySubtableDimension_WhenEntireHirearchyIsNotLoaded()
{
$this->assertApiResponseEqualsExpected("Referrers.getKeywords", array(
'idSite' => self::$fixture->idSite,
@@ -186,4 +186,4 @@ class PivotByQueryParamTest extends IntegrationTestCase
}
}
-PivotByQueryParamTest::$fixture = new ManyVisitsWithMockLocationProvider(); \ No newline at end of file
+PivotByQueryParamTest::$fixture = new ManyVisitsWithMockLocationProvider();
diff --git a/tests/PHPUnit/Integration/PrivacyManagerTest.php b/tests/PHPUnit/Integration/PrivacyManagerTest.php
index b78a93d32a..c229f08d8c 100644
--- a/tests/PHPUnit/Integration/PrivacyManagerTest.php
+++ b/tests/PHPUnit/Integration/PrivacyManagerTest.php
@@ -71,15 +71,9 @@ class PrivacyManagerTest extends IntegrationTestCase
{
parent::setUpBeforeClass();
- // Temporarily disable the purge of old archives so that getNumeric('nb_visits')
- // in _addReportData does not trigger the data purge of data we've just imported
- \Piwik\ArchiveProcessor\Rules::disablePurgeOutdatedArchives();
-
self::_addLogData();
self::_addReportData();
- \Piwik\ArchiveProcessor\Rules::enablePurgeOutdatedArchives();
-
self::$dbData = self::getDbTablesWithData();
}
diff --git a/tests/PHPUnit/Integration/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml b/tests/PHPUnit/Integration/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml
index ef32d680f8..32e753a325 100644
--- a/tests/PHPUnit/Integration/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml
+++ b/tests/PHPUnit/Integration/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml
@@ -4,7 +4,6 @@
<idSite>1</idSite>
<idVisit>35</idVisit>
<visitIp>194.57.91.215</visitIp>
- <userId>userid.email@example.org</userId>
<actionDetails>
<row>
@@ -33,7 +32,8 @@
-
+
+ <userId>userid.email@example.org</userId>
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -111,7 +111,6 @@
<idSite>1</idSite>
<idVisit>18</idVisit>
<visitIp>1.2.4.8</visitIp>
- <userId />
<actionDetails>
<row>
@@ -181,7 +180,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -267,7 +267,6 @@
<idSite>1</idSite>
<idVisit>17</idVisit>
<visitIp>1.2.4.8</visitIp>
- <userId />
<actionDetails>
<row>
@@ -306,7 +305,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -392,7 +392,6 @@
<idSite>1</idSite>
<idVisit>16</idVisit>
<visitIp>1.2.4.7</visitIp>
- <userId />
<actionDetails>
<row>
@@ -445,7 +444,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -531,7 +531,6 @@
<idSite>1</idSite>
<idVisit>15</idVisit>
<visitIp>1.2.4.7</visitIp>
- <userId />
<actionDetails>
<row>
@@ -570,7 +569,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -656,7 +656,6 @@
<idSite>1</idSite>
<idVisit>14</idVisit>
<visitIp>1.2.4.6</visitIp>
- <userId />
<actionDetails>
<row>
@@ -726,7 +725,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -812,7 +812,6 @@
<idSite>1</idSite>
<idVisit>13</idVisit>
<visitIp>1.2.4.6</visitIp>
- <userId />
<actionDetails>
<row>
@@ -851,7 +850,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -937,7 +937,6 @@
<idSite>1</idSite>
<idVisit>12</idVisit>
<visitIp>1.2.4.5</visitIp>
- <userId />
<actionDetails>
<row>
@@ -990,7 +989,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1076,7 +1076,6 @@
<idSite>1</idSite>
<idVisit>11</idVisit>
<visitIp>1.2.4.5</visitIp>
- <userId />
<actionDetails>
<row>
@@ -1115,7 +1114,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1201,7 +1201,6 @@
<idSite>1</idSite>
<idVisit>10</idVisit>
<visitIp>1.2.4.4</visitIp>
- <userId />
<actionDetails>
<row>
@@ -1271,7 +1270,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1357,7 +1357,6 @@
<idSite>1</idSite>
<idVisit>9</idVisit>
<visitIp>1.2.4.4</visitIp>
- <userId />
<actionDetails>
<row>
@@ -1396,7 +1395,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1482,7 +1482,6 @@
<idSite>1</idSite>
<idVisit>8</idVisit>
<visitIp>1.2.4.3</visitIp>
- <userId />
<actionDetails>
<row>
@@ -1535,7 +1534,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1621,7 +1621,6 @@
<idSite>1</idSite>
<idVisit>30</idVisit>
<visitIp>113.62.1.1</visitIp>
- <userId />
<actionDetails>
<row>
@@ -1674,7 +1673,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1752,7 +1752,6 @@
<idSite>1</idSite>
<idVisit>7</idVisit>
<visitIp>1.2.4.3</visitIp>
- <userId />
<actionDetails>
<row>
@@ -1791,7 +1790,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1877,7 +1877,6 @@
<idSite>1</idSite>
<idVisit>29</idVisit>
<visitIp>113.62.1.1</visitIp>
- <userId />
<actionDetails>
<row>
@@ -1916,7 +1915,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -2002,7 +2002,6 @@
<idSite>1</idSite>
<idVisit>6</idVisit>
<visitIp>1.2.4.2</visitIp>
- <userId />
<actionDetails>
<row>
@@ -2072,7 +2071,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -2158,7 +2158,6 @@
<idSite>1</idSite>
<idVisit>28</idVisit>
<visitIp>2001:db8:85a3::8a2e:370:7334</visitIp>
- <userId />
<actionDetails>
<row>
@@ -2228,7 +2227,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -2306,7 +2306,6 @@
<idSite>1</idSite>
<idVisit>5</idVisit>
<visitIp>1.2.4.2</visitIp>
- <userId />
<actionDetails>
<row>
@@ -2345,7 +2344,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -2431,7 +2431,6 @@
<idSite>1</idSite>
<idVisit>27</idVisit>
<visitIp>2001:db8:85a3::8a2e:370:7334</visitIp>
- <userId />
<actionDetails>
<row>
@@ -2470,7 +2469,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -2556,7 +2556,6 @@
<idSite>1</idSite>
<idVisit>4</idVisit>
<visitIp>1.2.4.1</visitIp>
- <userId />
<actionDetails>
<row>
@@ -2609,7 +2608,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -2695,7 +2695,6 @@
<idSite>1</idSite>
<idVisit>22</idVisit>
<visitIp>::ffff:137.82.130.49</visitIp>
- <userId />
<actionDetails>
<row>
@@ -2748,7 +2747,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -2834,7 +2834,6 @@
<idSite>1</idSite>
<idVisit>26</idVisit>
<visitIp>137.82.0.0</visitIp>
- <userId />
<actionDetails>
<row>
@@ -2887,7 +2886,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -2965,7 +2965,6 @@
<idSite>1</idSite>
<idVisit>34</idVisit>
<visitIp>103.29.196.229</visitIp>
- <userId />
<actionDetails>
<row>
@@ -3018,7 +3017,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -3104,7 +3104,6 @@
<idSite>1</idSite>
<idVisit>3</idVisit>
<visitIp>1.2.4.1</visitIp>
- <userId />
<actionDetails>
<row>
@@ -3143,7 +3142,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -3229,7 +3229,6 @@
<idSite>1</idSite>
<idVisit>21</idVisit>
<visitIp>::ffff:137.82.130.49</visitIp>
- <userId />
<actionDetails>
<row>
@@ -3268,7 +3267,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -3354,7 +3354,6 @@
<idSite>1</idSite>
<idVisit>25</idVisit>
<visitIp>137.82.0.0</visitIp>
- <userId />
<actionDetails>
<row>
@@ -3393,7 +3392,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -3479,7 +3479,6 @@
<idSite>1</idSite>
<idVisit>33</idVisit>
<visitIp>103.29.196.229</visitIp>
- <userId />
<actionDetails>
<row>
@@ -3518,7 +3517,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -3604,7 +3604,6 @@
<idSite>1</idSite>
<idVisit>2</idVisit>
<visitIp>1.2.4.0</visitIp>
- <userId />
<actionDetails>
<row>
@@ -3674,7 +3673,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -3760,7 +3760,6 @@
<idSite>1</idSite>
<idVisit>20</idVisit>
<visitIp>194.57.91.215</visitIp>
- <userId />
<actionDetails>
<row>
@@ -3830,7 +3829,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -3916,7 +3916,6 @@
<idSite>1</idSite>
<idVisit>24</idVisit>
<visitIp>137.82.130.0</visitIp>
- <userId />
<actionDetails>
<row>
@@ -3986,7 +3985,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -4064,7 +4064,6 @@
<idSite>1</idSite>
<idVisit>32</idVisit>
<visitIp>151.100.101.92</visitIp>
- <userId />
<actionDetails>
<row>
@@ -4134,7 +4133,8 @@
-
+
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -4220,7 +4220,6 @@
<idSite>1</idSite>
<idVisit>1</idVisit>
<visitIp>1.2.4.0</visitIp>
- <userId />
<actionDetails>
<row>
@@ -4259,7 +4258,8 @@
-
+
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4345,7 +4345,6 @@
<idSite>1</idSite>
<idVisit>19</idVisit>
<visitIp>194.57.91.215</visitIp>
- <userId />
<actionDetails>
<row>
@@ -4384,7 +4383,8 @@
-
+
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4470,7 +4470,6 @@
<idSite>1</idSite>
<idVisit>23</idVisit>
<visitIp>137.82.130.0</visitIp>
- <userId />
<actionDetails>
<row>
@@ -4509,7 +4508,8 @@
-
+
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4595,7 +4595,6 @@
<idSite>1</idSite>
<idVisit>31</idVisit>
<visitIp>151.100.101.92</visitIp>
- <userId />
<actionDetails>
<row>
@@ -4634,7 +4633,8 @@
-
+
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml
index 2c741a31b0..e058d9fe14 100644
--- a/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_day.xml
@@ -26,7 +26,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -128,7 +128,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -298,7 +298,7 @@
<searches>0</searches>
<actions>6</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -400,7 +400,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -737,7 +737,7 @@
<searches>0</searches>
<actions>15</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -839,7 +839,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -937,7 +937,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -1103,7 +1103,7 @@
<searches>0</searches>
<actions>6</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -1201,7 +1201,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -1534,7 +1534,7 @@
<searches>0</searches>
<actions>15</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml
index 2c741a31b0..e058d9fe14 100644
--- a/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml
+++ b/tests/PHPUnit/Integration/expected/test_CustomEvents__Live.getLastVisitsDetails_month.xml
@@ -26,7 +26,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -128,7 +128,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -298,7 +298,7 @@
<searches>0</searches>
<actions>6</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -400,7 +400,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -737,7 +737,7 @@
<searches>0</searches>
<actions>15</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -839,7 +839,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -937,7 +937,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -1103,7 +1103,7 @@
<searches>0</searches>
<actions>6</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -1201,7 +1201,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -1534,7 +1534,7 @@
<searches>0</searches>
<actions>15</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_ImportLogs__Live.getLastVisitsDetails_range.xml b/tests/PHPUnit/Integration/expected/test_ImportLogs__Live.getLastVisitsDetails_range.xml
index dfb3d76736..979dcb1146 100644
--- a/tests/PHPUnit/Integration/expected/test_ImportLogs__Live.getLastVisitsDetails_range.xml
+++ b/tests/PHPUnit/Integration/expected/test_ImportLogs__Live.getLastVisitsDetails_range.xml
@@ -42,7 +42,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -180,7 +180,7 @@
<searches>0</searches>
<actions>2</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -289,7 +289,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -439,7 +439,7 @@
<searches>0</searches>
<actions>2</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -554,7 +554,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>0</visitConverted>
@@ -680,7 +680,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -813,7 +813,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -935,7 +935,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1049,7 +1049,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1218,7 +1218,7 @@
<searches>0</searches>
<actions>3</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -1336,7 +1336,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1462,7 +1462,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -1750,7 +1750,7 @@
<searches>0</searches>
<actions>10</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -1862,7 +1862,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -1979,7 +1979,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -2112,7 +2112,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -2237,7 +2237,7 @@
<searches>0</searches>
<actions>2</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -2369,7 +2369,7 @@
<searches>0</searches>
<actions>3</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -2477,7 +2477,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -2585,7 +2585,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -2683,7 +2683,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -2791,7 +2791,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -2899,7 +2899,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3007,7 +3007,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3111,7 +3111,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3215,7 +3215,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3319,7 +3319,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3423,7 +3423,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3527,7 +3527,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3631,7 +3631,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3735,7 +3735,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3839,7 +3839,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -3943,7 +3943,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4047,7 +4047,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4151,7 +4151,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4255,7 +4255,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4359,7 +4359,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4463,7 +4463,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4567,7 +4567,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -4671,7 +4671,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisitAsc__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisitAsc__Live.getLastVisitsDetails_month.xml
index 050f358af4..314b95c25a 100644
--- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisitAsc__Live.getLastVisitsDetails_month.xml
+++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisitAsc__Live.getLastVisitsDetails_month.xml
@@ -43,7 +43,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -101,8 +101,6 @@
<regionCode>P3</regionCode>
<city>Stratford-upon-Avon</city>
<location>Stratford-upon-Avon, Warwickshire, United Kingdom</location>
- <latitude>123.456001</latitude>
- <longitude>21.320999</longitude>
<visitLocalTime>12:34:06</visitLocalTime>
<visitLocalHour>12</visitLocalHour>
<daysSinceLastVisit>0</daysSinceLastVisit>
@@ -199,7 +197,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -257,8 +255,6 @@
<regionCode>P3</regionCode>
<city>Stratford-upon-Avon</city>
<location>Stratford-upon-Avon, Warwickshire, United Kingdom</location>
- <latitude>123.456001</latitude>
- <longitude>21.320999</longitude>
<visitLocalTime>12:34:06</visitLocalTime>
<visitLocalHour>12</visitLocalHour>
<daysSinceLastVisit>10</daysSinceLastVisit>
@@ -324,7 +320,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -382,8 +378,6 @@
<regionCode>A6</regionCode>
<city>Besançon</city>
<location>Besançon, Franche-Comte, France</location>
- <latitude>47.249001</latitude>
- <longitude>6.018000</longitude>
<visitLocalTime>12:34:06</visitLocalTime>
<visitLocalHour>12</visitLocalHour>
<daysSinceLastVisit>0</daysSinceLastVisit>
@@ -480,7 +474,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -538,8 +532,6 @@
<regionCode>A6</regionCode>
<city>Besançon</city>
<location>Besançon, Franche-Comte, France</location>
- <latitude>47.249001</latitude>
- <longitude>6.018000</longitude>
<visitLocalTime>12:34:06</visitLocalTime>
<visitLocalHour>12</visitLocalHour>
<daysSinceLastVisit>10</daysSinceLastVisit>
@@ -605,7 +597,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -663,8 +655,6 @@
<regionCode>BC</regionCode>
<city>Vancouver</city>
<location>Vancouver, British Columbia, Canada</location>
- <latitude>49.250000</latitude>
- <longitude>-123.133003</longitude>
<visitLocalTime>12:34:06</visitLocalTime>
<visitLocalHour>12</visitLocalHour>
<daysSinceLastVisit>0</daysSinceLastVisit>
@@ -761,7 +751,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -819,8 +809,6 @@
<regionCode>BC</regionCode>
<city>Vancouver</city>
<location>Vancouver, British Columbia, Canada</location>
- <latitude>49.250000</latitude>
- <longitude>-123.133003</longitude>
<visitLocalTime>12:34:06</visitLocalTime>
<visitLocalHour>12</visitLocalHour>
<daysSinceLastVisit>10</daysSinceLastVisit>
@@ -878,7 +866,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -936,8 +924,6 @@
<regionCode />
<city />
<location>Italy</location>
- <latitude />
- <longitude />
<visitLocalTime>12:34:06</visitLocalTime>
<visitLocalHour>12</visitLocalHour>
<daysSinceLastVisit>0</daysSinceLastVisit>
diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml
index 6f86215248..3b6d2c7b2d 100644
--- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml
+++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByIdVisit__Live.getLastVisitsDetails_month.xml
@@ -181,7 +181,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -306,7 +306,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -445,7 +445,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -570,7 +570,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -726,7 +726,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -851,7 +851,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByVisitCount__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByVisitCount__Live.getLastVisitsDetails_month.xml
index e9caa3ba66..bf27da9995 100644
--- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByVisitCount__Live.getLastVisitsDetails_month.xml
+++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortByVisitCount__Live.getLastVisitsDetails_month.xml
@@ -74,7 +74,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -199,7 +199,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -355,7 +355,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -494,7 +494,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -619,7 +619,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -744,7 +744,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml
index 6f86215248..3b6d2c7b2d 100644
--- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml
+++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest_Live.getLastVisitsDetails_sortDesc__Live.getLastVisitsDetails_month.xml
@@ -181,7 +181,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -306,7 +306,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -445,7 +445,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -570,7 +570,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -726,7 +726,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -851,7 +851,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml
index fc8478d9b3..d4f1c0f44f 100644
--- a/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml
+++ b/tests/PHPUnit/Integration/expected/test_ManyVisitorsOneWebsiteTest__Live.getLastVisitsDetails_month.xml
@@ -181,7 +181,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -306,7 +306,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -445,7 +445,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -570,7 +570,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -726,7 +726,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -851,7 +851,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -990,7 +990,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1115,7 +1115,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -1271,7 +1271,7 @@
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Live.getLastVisitsDetails_day.xml
index d8a06d259e..dca93d3104 100644
--- a/tests/PHPUnit/Integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Live.getLastVisitsDetails_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_OneVisitorTwoVisits_withCookieSupport__Live.getLastVisitsDetails_day.xml
@@ -36,7 +36,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -224,7 +224,7 @@
<searches>1</searches>
<actions>8</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_OneVisitor_NoKeywordSpecified__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_OneVisitor_NoKeywordSpecified__Live.getLastVisitsDetails_day.xml
index d6cf0ea497..5fde4bea23 100644
--- a/tests/PHPUnit/Integration/expected/test_OneVisitor_NoKeywordSpecified__Live.getLastVisitsDetails_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_OneVisitor_NoKeywordSpecified__Live.getLastVisitsDetails_day.xml
@@ -4,7 +4,6 @@
<idSite>1</idSite>
<idVisit>2</idVisit>
<visitIp>156.5.0.0</visitIp>
- <userId />
<actionDetails>
<row>
@@ -24,6 +23,7 @@
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -101,7 +101,6 @@
<idSite>1</idSite>
<idVisit>1</idVisit>
<visitIp>156.5.0.0</visitIp>
- <userId />
<actionDetails>
<row>
@@ -131,6 +130,7 @@
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml
index d40e18c7d9..eda23c25e7 100644
--- a/tests/PHPUnit/Integration/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml
+++ b/tests/PHPUnit/Integration/expected/test_OneVisitor_SeveralDays_ImportedInRandomOrderTest_shouldShowOneVisit_InEachOfThreeDays__Live.getLastVisitsDetails_month.xml
@@ -30,7 +30,7 @@
<lastActionDateTime>2013-04-07 10:00:00</lastActionDateTime>
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -123,7 +123,7 @@
<lastActionDateTime>2013-04-06 11:00:00</lastActionDateTime>
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -216,7 +216,7 @@
<lastActionDateTime>2013-04-05 12:00:00</lastActionDateTime>
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_CreatesCorrectPivotTable_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml b/tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml
index 6ab5630422..6ab5630422 100644
--- a/tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_CreatesCorrectPivotTable_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml
+++ b/tests/PHPUnit/Integration/expected/test_PivotByQueryParamTest_test_PivotBySubtableDimension_WhenEntireHirearchyIsNotLoaded__Referrers.getKeywords_week.xml
diff --git a/tests/PHPUnit/Integration/expected/test_TimezonesTest__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_TimezonesTest__Live.getLastVisitsDetails_day.xml
index 35533fdac6..ea4cebdafe 100644
--- a/tests/PHPUnit/Integration/expected/test_TimezonesTest__Live.getLastVisitsDetails_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_TimezonesTest__Live.getLastVisitsDetails_day.xml
@@ -25,7 +25,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html
index 25de6d9057..8f057be5e7 100644
--- a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html
+++ b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html
@@ -145,6 +145,16 @@
</a>
</li>
<li>
+ <a href="#Contents_getContentNames" style="text-decoration:none; color: rgb(68,68,68);">
+ Content Name
+ </a>
+ </li>
+ <li>
+ <a href="#Contents_getContentPieces" style="text-decoration:none; color: rgb(68,68,68);">
+ Content Piece
+ </a>
+ </li>
+ <li>
<a href="#Events_getCategory" style="text-decoration:none; color: rgb(68,68,68);">
Event Categories
</a>
@@ -438,7 +448,7 @@
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
Users </td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
- 1
+ 0
</td>
</tr>
@@ -3706,6 +3716,16 @@
</h2>
There is no data for this report.
+<h2 id="Contents_getContentNames" style="color: rgb(126,115,99); font-size: 11pt;">
+ Content Name
+</h2>
+
+ There is no data for this report.
+<h2 id="Contents_getContentPieces" style="color: rgb(126,115,99); font-size: 11pt;">
+ Content Piece
+</h2>
+
+ There is no data for this report.
<h2 id="Events_getCategory" style="color: rgb(126,115,99); font-size: 11pt;">
Event Categories
</h2>
diff --git a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf
index 5fecafa369..7891c7d5ca 100644
--- a/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf
+++ b/tests/PHPUnit/Integration/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf
Binary files differ
diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml
index 1946cec5ba..cc28b360a0 100644
--- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml
+++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__Live.getLastVisitsDetails_month.xml
@@ -16,7 +16,7 @@
</actionDetails>
<lastActionDateTime>2010-03-06 11:22:33</lastActionDateTime>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
</row>
<row>
<idVisit>2</idVisit>
@@ -45,7 +45,7 @@
</actionDetails>
<lastActionDateTime>2010-03-06 11:28:33</lastActionDateTime>
<actions>2</actions>
- <userId>0</userId>
+ <userId />
</row>
<row>
<idVisit>3</idVisit>
@@ -53,11 +53,22 @@
<actionDetails>
<row>
<type>action</type>
+ <url>http://example.org/no-user-id-set-but-should-appear-in-user-id-visit</url>
+ <pageTitle>no User Id set but it should appear in email@example.com!</pageTitle>
+ <pageIdAction>6</pageIdAction>
+ <serverTimePretty>Sat 6 Mar 13:16:33</serverTimePretty>
+ <pageId>4</pageId>
+ <timeSpent>360</timeSpent>
+ <timeSpentPretty>6 min 0s</timeSpentPretty>
+ <icon />
+ </row>
+ <row>
+ <type>action</type>
<url>http://example.org/index.htm</url>
<pageTitle>incredible title!</pageTitle>
<pageIdAction>2</pageIdAction>
<serverTimePretty>Sat 6 Mar 13:22:33</serverTimePretty>
- <pageId>4</pageId>
+ <pageId>5</pageId>
<timeSpent>360</timeSpent>
<timeSpentPretty>6 min 0s</timeSpentPretty>
<icon />
@@ -68,12 +79,12 @@
<pageTitle>second page</pageTitle>
<pageIdAction>2</pageIdAction>
<serverTimePretty>Sat 6 Mar 13:28:33</serverTimePretty>
- <pageId>5</pageId>
+ <pageId>6</pageId>
<icon />
</row>
</actionDetails>
<lastActionDateTime>2010-03-06 13:28:33</lastActionDateTime>
- <actions>2</actions>
+ <actions>3</actions>
<userId>email@example.com</userId>
</row>
<row>
@@ -86,7 +97,7 @@
<pageTitle>a new user id was set -&gt; new visit</pageTitle>
<pageIdAction>2</pageIdAction>
<serverTimePretty>Sat 6 Mar 13:34:33</serverTimePretty>
- <pageId>6</pageId>
+ <pageId>7</pageId>
<icon />
</row>
</actionDetails>
@@ -102,9 +113,9 @@
<type>action</type>
<url>http://example.org/home</url>
<pageTitle>same user id was set -&gt; this is the same unique user</pageTitle>
- <pageIdAction>8</pageIdAction>
+ <pageIdAction>10</pageIdAction>
<serverTimePretty>Sat 6 Mar 16:22:33</serverTimePretty>
- <pageId>7</pageId>
+ <pageId>8</pageId>
<timeSpent>360</timeSpent>
<timeSpentPretty>6 min 0s</timeSpentPretty>
<icon />
@@ -113,9 +124,9 @@
<type>action</type>
<url>http://example.org/home</url>
<pageTitle>second pageview - by this user id</pageTitle>
- <pageIdAction>8</pageIdAction>
+ <pageIdAction>10</pageIdAction>
<serverTimePretty>Sat 6 Mar 16:28:33</serverTimePretty>
- <pageId>8</pageId>
+ <pageId>9</pageId>
<icon />
</row>
<row>
@@ -157,15 +168,15 @@
<type>action</type>
<url>http://example.org/home</url>
<pageTitle>pageview - should not be tracked by our user id but in a new visit</pageTitle>
- <pageIdAction>8</pageIdAction>
+ <pageIdAction>10</pageIdAction>
<serverTimePretty>Sat 6 Mar 16:28:33</serverTimePretty>
- <pageId>9</pageId>
+ <pageId>10</pageId>
<icon />
</row>
</actionDetails>
<lastActionDateTime>2010-03-06 16:28:33</lastActionDateTime>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
</row>
<row>
<idVisit>7</idVisit>
@@ -177,7 +188,7 @@
<pageTitle>Page view by email@example.com</pageTitle>
<pageIdAction>2</pageIdAction>
<serverTimePretty>Sun 14 Mar 11:22:33</serverTimePretty>
- <pageId>10</pageId>
+ <pageId>11</pageId>
<icon />
</row>
</actionDetails>
@@ -195,7 +206,7 @@
<pageTitle>A page view by new-user-id@one-weeklater</pageTitle>
<pageIdAction>2</pageIdAction>
<serverTimePretty>Sun 14 Mar 11:46:33</serverTimePretty>
- <pageId>11</pageId>
+ <pageId>12</pageId>
<icon />
</row>
</actionDetails>
diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_day.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_day.xml
index 4a8baf7fc3..ca85e8dede 100644
--- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_day.xml
@@ -3,12 +3,12 @@
<nb_uniq_visitors>5</nb_uniq_visitors>
<nb_users>2</nb_users>
<nb_visits>6</nb_visits>
- <nb_actions>9</nb_actions>
+ <nb_actions>10</nb_actions>
<nb_visits_converted>1</nb_visits_converted>
<bounce_count>3</bounce_count>
- <sum_visit_length>1623</sum_visit_length>
- <max_actions>2</max_actions>
+ <sum_visit_length>1983</sum_visit_length>
+ <max_actions>3</max_actions>
<bounce_rate>50%</bounce_rate>
- <nb_actions_per_visit>1.5</nb_actions_per_visit>
- <avg_time_on_site>271</avg_time_on_site>
+ <nb_actions_per_visit>1.7</nb_actions_per_visit>
+ <avg_time_on_site>331</avg_time_on_site>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_month.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_month.xml
index 94ed6f59e0..7731535795 100644
--- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_month.xml
+++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_month.xml
@@ -3,12 +3,12 @@
<nb_uniq_visitors>6</nb_uniq_visitors>
<nb_users>3</nb_users>
<nb_visits>8</nb_visits>
- <nb_actions>11</nb_actions>
+ <nb_actions>12</nb_actions>
<nb_visits_converted>1</nb_visits_converted>
<bounce_count>5</bounce_count>
- <sum_visit_length>1623</sum_visit_length>
- <max_actions>2</max_actions>
+ <sum_visit_length>1983</sum_visit_length>
+ <max_actions>3</max_actions>
<bounce_rate>63%</bounce_rate>
- <nb_actions_per_visit>1.4</nb_actions_per_visit>
- <avg_time_on_site>203</avg_time_on_site>
+ <nb_actions_per_visit>1.5</nb_actions_per_visit>
+ <avg_time_on_site>248</avg_time_on_site>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_week.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_week.xml
index 4a8baf7fc3..ca85e8dede 100644
--- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_week.xml
+++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_week.xml
@@ -3,12 +3,12 @@
<nb_uniq_visitors>5</nb_uniq_visitors>
<nb_users>2</nb_users>
<nb_visits>6</nb_visits>
- <nb_actions>9</nb_actions>
+ <nb_actions>10</nb_actions>
<nb_visits_converted>1</nb_visits_converted>
<bounce_count>3</bounce_count>
- <sum_visit_length>1623</sum_visit_length>
- <max_actions>2</max_actions>
+ <sum_visit_length>1983</sum_visit_length>
+ <max_actions>3</max_actions>
<bounce_rate>50%</bounce_rate>
- <nb_actions_per_visit>1.5</nb_actions_per_visit>
- <avg_time_on_site>271</avg_time_on_site>
+ <nb_actions_per_visit>1.7</nb_actions_per_visit>
+ <avg_time_on_site>331</avg_time_on_site>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_year.xml b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_year.xml
index 107fa6ae8a..84850ee827 100644
--- a/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_year.xml
+++ b/tests/PHPUnit/Integration/expected/test_UserId_VisitorId__VisitsSummary.get_year.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>
<nb_visits>8</nb_visits>
- <nb_actions>11</nb_actions>
+ <nb_actions>12</nb_actions>
<nb_visits_converted>1</nb_visits_converted>
<bounce_count>5</bounce_count>
- <sum_visit_length>1623</sum_visit_length>
- <max_actions>2</max_actions>
+ <sum_visit_length>1983</sum_visit_length>
+ <max_actions>3</max_actions>
<bounce_rate>63%</bounce_rate>
- <nb_actions_per_visit>1.4</nb_actions_per_visit>
- <avg_time_on_site>203</avg_time_on_site>
+ <nb_actions_per_visit>1.5</nb_actions_per_visit>
+ <avg_time_on_site>248</avg_time_on_site>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml
index df83d3ad4d..f45437858d 100644
--- a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_LiveEcommerceStatusOrdered__Live.getLastVisitsDetails_day.xml
@@ -51,7 +51,7 @@
<searches>0</searches>
<actions>0</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -262,7 +262,7 @@
<searches>0</searches>
<actions>3</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml
index db2945b307..37e6dd50a1 100644
--- a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml
+++ b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems__Live.getLastVisitsDetails_day.xml
@@ -127,7 +127,7 @@
<searches>0</searches>
<actions>3</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returningCustomer</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>0</visitConverted>
@@ -457,7 +457,7 @@
<searches>0</searches>
<actions>6</actions>
- <userId>0</userId>
+ <userId />
<visitorType>returning</visitorType>
<visitorTypeIcon>plugins/Live/images/returningVisitor.gif</visitorTypeIcon>
<visitConverted>1</visitConverted>
@@ -677,7 +677,7 @@
<searches>0</searches>
<actions>4</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html
index b92f3087c5..401a3a99bb 100644
--- a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html
+++ b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html
@@ -190,6 +190,16 @@
</a>
</li>
<li>
+ <a href="#Contents_getContentNames" style="text-decoration:none; color: rgb(68,68,68);">
+ Content Name
+ </a>
+ </li>
+ <li>
+ <a href="#Contents_getContentPieces" style="text-decoration:none; color: rgb(68,68,68);">
+ Content Piece
+ </a>
+ </li>
+ <li>
<a href="#Events_getCategory" style="text-decoration:none; color: rgb(68,68,68);">
Event Categories
</a>
@@ -498,7 +508,7 @@
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
Users </td>
<td style="font-size: 11pt; border-bottom: 1px solid rgb(231,231,231); padding: 5px 0 5px 5px;">
- 1
+ 0
</td>
</tr>
@@ -4807,6 +4817,16 @@
</h2>
There is no data for this report.
+<h2 id="Contents_getContentNames" style="color: rgb(126,115,99); font-size: 11pt;">
+ Content Name
+</h2>
+
+ There is no data for this report.
+<h2 id="Contents_getContentPieces" style="color: rgb(126,115,99); font-size: 11pt;">
+ Content Piece
+</h2>
+
+ There is no data for this report.
<h2 id="Events_getCategory" style="color: rgb(126,115,99); font-size: 11pt;">
Event Categories
</h2>
diff --git a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf
index 0bb4a30c17..e8e37848fd 100644
--- a/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf
+++ b/tests/PHPUnit/Integration/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf
Binary files differ
diff --git a/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml b/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml
index b5052f4f74..38191d26d9 100644
--- a/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml
+++ b/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getLastVisitsDetails_range.xml
@@ -25,7 +25,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -140,7 +140,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
@@ -311,7 +311,7 @@
<searches>0</searches>
<actions>3</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getVisitorProfile.xml b/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getVisitorProfile.xml
index 0725631cd9..ff0e385988 100644
--- a/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getVisitorProfile.xml
+++ b/tests/PHPUnit/Integration/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__Live.getVisitorProfile.xml
@@ -68,7 +68,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>0</visitConverted>
@@ -184,7 +184,7 @@
<searches>0</searches>
<actions>1</actions>
- <userId>0</userId>
+ <userId />
<visitorType>new</visitorType>
<visitorTypeIcon />
<visitConverted>1</visitConverted>
diff --git a/tests/PHPUnit/IntegrationTestCase.php b/tests/PHPUnit/IntegrationTestCase.php
index ae8a674ed2..d5d78128a4 100755
--- a/tests/PHPUnit/IntegrationTestCase.php
+++ b/tests/PHPUnit/IntegrationTestCase.php
@@ -414,10 +414,6 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
{
$testConfig = new ApiTestConfig($params);
- // make sure that the reports we process here are not directly deleted in ArchiveProcessor/PluginsArchiver
- // (because we process reports in the past, they would sometimes be invalid, and would have been deleted)
- \Piwik\ArchiveProcessor\Rules::disablePurgeOutdatedArchives();
-
$testName = 'test_' . static::getOutputPrefix();
$this->missingExpectedFiles = array();
$this->comparisonFailures = array();
@@ -440,9 +436,6 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
$this->_testApiUrl($testName . $testConfig->testSuffix, $apiId, $requestUrl, $testConfig->compareAgainst, $testConfig->xmlFieldsToRemove, $params);
}
- // Restore normal purge behavior
- \Piwik\ArchiveProcessor\Rules::enablePurgeOutdatedArchives();
-
// change the language back to en
if ($this->lastLanguage != 'en') {
$this->changeLanguage('en');
@@ -525,7 +518,6 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase
*/
protected static function restoreDbTables($tables)
{
- // truncate existing tables
DbHelper::truncateAllTables();
// insert data
diff --git a/tests/PHPUnit/TestingEnvironment.php b/tests/PHPUnit/TestingEnvironment.php
index ba15855f61..0c5ddbc69e 100644
--- a/tests/PHPUnit/TestingEnvironment.php
+++ b/tests/PHPUnit/TestingEnvironment.php
@@ -88,7 +88,7 @@ class Piwik_TestingEnvironment
if (isset($_SERVER['QUERY_STRING'])
&& !$this->dontUseTestConfig
) {
- \Piwik\Log::verbose("Test Environment Variables for (%s):\n%s", $_SERVER['QUERY_STRING'], print_r($this->behaviorOverrideProperties, true));
+ @\Piwik\Log::verbose("Test Environment Variables for (%s):\n%s", $_SERVER['QUERY_STRING'], print_r($this->behaviorOverrideProperties, true));
}
} catch (Exception $ex) {
// ignore
diff --git a/tests/PHPUnit/UI b/tests/PHPUnit/UI
-Subproject d67cb0957de65891ab2e5b6fe12e04cdd35e1bf
+Subproject d98325f48a495a6d5d71cb1ffed231f6834f30b
diff --git a/tests/PHPUnit/bootstrap.php b/tests/PHPUnit/bootstrap.php
index dc16386563..2fd701a0e8 100644
--- a/tests/PHPUnit/bootstrap.php
+++ b/tests/PHPUnit/bootstrap.php
@@ -34,8 +34,9 @@ require_once file_exists(PIWIK_INCLUDE_PATH . '/vendor/autoload.php')
require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';
require_once PIWIK_INCLUDE_PATH . '/core/testMinimumPhpVersion.php';
require_once PIWIK_INCLUDE_PATH . '/core/FrontController.php';
-require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/DatabaseTestCase.php';
+require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Fixture.php';
require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/IntegrationTestCase.php';
+require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/DatabaseTestCase.php';
require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/ConsoleCommandTestCase.php';
require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/FakeAccess.php';
require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/MockPiwikOption.php';
@@ -49,8 +50,6 @@ if (getenv('PIWIK_USE_XHPROF') == 1) {
}
// require test fixtures
-require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Fixture.php';
-
$fixturesToLoad = array(
'/tests/PHPUnit/Fixtures/*.php',
'/tests/PHPUnit/UI/Fixtures/*.php',
diff --git a/tests/PHPUnit/proxy/includes.php b/tests/PHPUnit/proxy/includes.php
index 858ae485d2..b813fd21ad 100644
--- a/tests/PHPUnit/proxy/includes.php
+++ b/tests/PHPUnit/proxy/includes.php
@@ -28,7 +28,3 @@ require_once $vendorDirectory . '/piwik/device-detector/DeviceDetector.php';
\Piwik\SettingsServer::setMaxExecutionTime(0);
-// Make sure Data processed in cron core:archive command is not being purged instantly (useful for: Integration/ArchiveCronTest)
-if(\Piwik\SettingsServer::isArchivePhpTriggered()) {
- \Piwik\ArchiveProcessor\Rules::disablePurgeOutdatedArchives();
-} \ No newline at end of file
diff --git a/tests/resources/staticFileServer.php b/tests/resources/staticFileServer.php
index 5907c169e3..6ad277863e 100644
--- a/tests/resources/staticFileServer.php
+++ b/tests/resources/staticFileServer.php
@@ -15,7 +15,7 @@ use Piwik\ProxyHttp;
define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__).'/../../');
if(file_exists(PIWIK_DOCUMENT_ROOT . '/bootstrap.php'))
{
- require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php';
+ require_once PIWIK_DOCUMENT_ROOT . '/bootstrap.php';
}
error_reporting(E_ALL|E_NOTICE);
@@ -25,11 +25,11 @@ error_reporting(E_ALL|E_NOTICE);
if(!defined('PIWIK_USER_PATH'))
{
- define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
+ define('PIWIK_USER_PATH', PIWIK_DOCUMENT_ROOT);
}
if(!defined('PIWIK_INCLUDE_PATH'))
{
- define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT);
+ define('PIWIK_INCLUDE_PATH', PIWIK_DOCUMENT_ROOT);
}
require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';